Merge tag 'android-11.0.0_r16' into android-10.0
Android 11.0.0 release 16 - twrp bringup patch
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 0000000..540e149
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,23 @@
+- [ ] I am running an official build of TWRP, downloaded from https://twrp.me/Devices/
+- [ ] I am running the latest version of TWRP
+- [ ] I have read the FAQ (https://twrp.me/FAQ/)
+- [ ] I have searched for my issue and it does not already exist
+
+**Device codename**: <!-- Device codename -->
+**TWRP version**: <!-- TWRP version installed -->
+
+#### WHAT STEPS WILL REPRODUCE THE PROBLEM?
+<!-- Explain the steps necessary to reproduce the problem, as completely as possible -->
+
+#### WHAT IS THE EXPECTED RESULT?
+<!-- Explain what the expected result is, as completely as possible -->
+
+#### WHAT HAPPENS INSTEAD?
+<!-- Explain what happens instead, as completely as possible -->
+
+#### ADDITIONAL INFORMATION
+<!-- Add any additional information you know about the issue, such as possible causes and solutions -->
+
+<!-- Use https://paste.omnirom.org/ and upload `/tmp/recovery.log` and the output of `dmesg` -->
+`/tmp/recovery.log`: <!-- Link here -->
+`dmesg`: <!-- Link here -->
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 0000000..d04c212
--- /dev/null
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,7 @@
+# WE DO NOT MERGE PULL REQUESTS SUBMITTED HERE
+
+You will need to submit it through [OmniRom Gerrit](https://gerrit.omnirom.org/#/admin/projects/android_bootable_recovery/)
+
+For changes to device trees, use [TWRP Gerrit](https://gerrit.twrp.me/)
+
+This guide explani how to use [Gerrit code review](https://forum.xda-developers.com/general/xda-university/guide-using-gerrit-code-review-t3720802)
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..480f547
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+.*.swp
+*~
+tags
+.vscode
diff --git a/Android.bp b/Android.bp
old mode 100644
new mode 100755
index 8e29dc8..250ef9d
--- a/Android.bp
+++ b/Android.bp
@@ -1,203 +1,90 @@
-// Copyright (C) 2018 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
+subdirs = [
+ "bootloader_message",
+]
cc_defaults {
name: "recovery_defaults",
-
cflags: [
"-D_FILE_OFFSET_BITS=64",
-
// Must be the same as RECOVERY_API_VERSION.
"-DRECOVERY_API_VERSION=3",
-
"-Wall",
"-Werror",
],
+ cpp_std: "c++17",
}
-cc_library_static {
- name: "librecovery_fastboot",
- recovery_available: true,
- defaults: [
- "recovery_defaults",
+bootstrap_go_package {
+ name: "soong-libaosprecovery_defaults",
+ pkgPath: "bootable/recovery/libaosprecovery",
+ deps: [
+ "soong",
+ "soong-android",
+ "soong-cc"
],
-
srcs: [
- "fastboot/fastboot.cpp",
+ "libaosprecovery_defaults.go"
],
-
- shared_libs: [
- "libbase",
- "libbootloader_message",
- "libcutils",
- "liblog",
- "librecovery_ui",
- ],
-
- static_libs: [
- "librecovery_ui_default",
- ],
+ pluginFor: ["soong_build"]
}
-cc_defaults {
- name: "librecovery_defaults",
+libaosprecovery_defaults {
+ name: "libaosprecovery_defaults"
+}
- defaults: [
- "recovery_defaults",
+cc_library_shared {
+ name: "libaosprecovery",
+ defaults: ["libaosprecovery_defaults"],
+ cflags: [
+ "-std=gnu++2a",
+ "-DRECOVERY_API_VERSION=3"
],
-
+ include_dirs: [
+ "bootable/recovery/install/include",
+ "bootable/recovery/recovery_ui/include",
+ "bootable/recovery/recovery_utils/include",
+ "bootable/recovery/otautil/include",
+ "bootable/recovery/minadbd",
+ "bootable/recovery/minadbd/include",
+ "bootable/recovery/minzip",
+ "bootable/recovery/twrpinstall/include",
+ "system/libvintf/include"
+ ],
+ srcs: [
+ "install/adb_install.cpp",
+ "install/asn1_decoder.cpp",
+ "install/get_args.cpp",
+ "install/install.cpp",
+ "install/package.cpp",
+ "install/verifier.cpp",
+ "install/wipe_data.cpp",
+ "install/set_metadata.cpp",
+ "install/ZipUtil.cpp"
+ ],
shared_libs: [
- "android.hardware.boot@1.0",
- "android.hardware.boot@1.1",
"libbase",
"libbootloader_message",
"libcrypto",
- "libcutils",
- "libfs_mgr",
- "liblp",
+ "libext4_utils",
+ "libfs_mgr",
+ "libfusesideload",
+ "libhidl-gen-utils",
+ "libhidlbase",
"liblog",
- "libprotobuf-cpp-lite",
+ "libselinux",
+ "libtinyxml2",
+ "libutils",
+ "libz",
"libziparchive",
+ "libcutils",
+ "libc++"
],
-
static_libs: [
- "libc++fs",
- "libinstall",
- "librecovery_fastboot",
- "libminui",
- "librecovery_utils",
"libotautil",
- "libsnapshot_nobinder",
- ],
-}
-
-cc_library_static {
- name: "librecovery",
- recovery_available: true,
-
- defaults: [
- "librecovery_defaults",
- ],
-
- srcs: [
- "recovery.cpp",
- ],
-
- shared_libs: [
- "librecovery_ui",
- ],
-}
-
-prebuilt_etc {
- name: "init_recovery.rc",
- filename: "init.rc",
- src: "etc/init.rc",
- sub_dir: "init/hw",
- recovery: true,
-}
-
-cc_binary {
- name: "recovery",
- recovery: true,
-
- defaults: [
- "libinstall_defaults",
- "librecovery_defaults",
- "librecovery_utils_defaults",
- ],
-
- srcs: [
- "recovery_main.cpp",
- ],
-
- shared_libs: [
- "librecovery_ui",
- ],
-
- static_libs: [
- "librecovery",
- "librecovery_ui_default",
- ],
-
- required: [
- "e2fsdroid.recovery",
- "init_recovery.rc",
- "librecovery_ui_ext",
- "minadbd",
- "mke2fs.conf.recovery",
- "mke2fs.recovery",
- "recovery_deps",
- "ueventd.rc.recovery",
- ],
-}
-
-// The dynamic executable that runs after /data mounts.
-cc_binary {
- name: "recovery-persist",
-
- defaults: [
- "recovery_defaults",
- ],
-
- srcs: [
- "recovery-persist.cpp",
- ],
-
- shared_libs: [
- "libbase",
- "liblog",
- ],
-
- static_libs: [
+ "libvintf",
+ "libhidl-gen-utils",
"librecovery_utils",
- ],
-
- init_rc: [
- "recovery-persist.rc",
- ],
+ "libc++fs"
+ ]
}
-// The dynamic executable that runs at init.
-cc_binary {
- name: "recovery-refresh",
-
- defaults: [
- "recovery_defaults",
- ],
-
- srcs: [
- "recovery-refresh.cpp",
- ],
-
- shared_libs: [
- "libbase",
- "liblog",
- ],
-
- static_libs: [
- "librecovery_utils",
- ],
-
- init_rc: [
- "recovery-refresh.rc",
- ],
-}
-
-filegroup {
- name: "res-testdata",
-
- srcs: [
- "res-*/images/*_text.png",
- ],
-}
diff --git a/Android.mk b/Android.mk
old mode 100644
new mode 100755
index 9f69153..f81d023
--- a/Android.mk
+++ b/Android.mk
@@ -13,66 +13,633 @@
# limitations under the License.
LOCAL_PATH := $(call my-dir)
+commands_TWRP_local_path := $(LOCAL_PATH)
-# Needed by build/make/core/Makefile. Must be consistent with the value in Android.bp.
+ifneq ($(project-path-for),)
+ ifeq ($(LOCAL_PATH),$(call project-path-for,recovery))
+ PROJECT_PATH_AGREES := true
+ BOARD_SEPOLICY_DIRS += $(call project-path-for,recovery)/sepolicy
+ endif
+else
+ ifeq ($(LOCAL_PATH),bootable/recovery)
+ PROJECT_PATH_AGREES := true
+ BOARD_SEPOLICY_DIRS += bootable/recovery/sepolicy
+ else
+ ifeq ($(LOCAL_PATH),bootable/recovery-twrp)
+ ifeq ($(RECOVERY_VARIANT),twrp)
+ PROJECT_PATH_AGREES := true
+ BOARD_SEPOLICY_DIRS += bootable/recovery-twrp/sepolicy
+ endif
+ endif
+ endif
+endif
+
+ifeq ($(PROJECT_PATH_AGREES),true)
+
+ifeq ($(CM_PLATFORM_SDK_VERSION),)
+ CM_PLATFORM_SDK_VERSION := 0
+endif
+
+include $(CLEAR_VARS)
+
+TWRES_PATH := /twres/
+TWHTCD_PATH := $(TWRES_PATH)htcd/
+
+TARGET_RECOVERY_GUI := true
+
+LOCAL_STATIC_LIBRARIES :=
+LOCAL_SHARED_LIBRARIES :=
+
+ifneq ($(TW_DEVICE_VERSION),)
+ LOCAL_CFLAGS += -DTW_DEVICE_VERSION='"-$(TW_DEVICE_VERSION)"'
+else
+ LOCAL_CFLAGS += -DTW_DEVICE_VERSION='"-0"'
+endif
+LOCAL_CFLAGS += -DPLATFORM_SDK_VERSION=$(PLATFORM_SDK_VERSION)
+
+LOCAL_SRC_FILES := \
+ twrp.cpp \
+ fixContexts.cpp \
+ twrpTar.cpp \
+ exclude.cpp \
+ find_file.cpp \
+ infomanager.cpp \
+ data.cpp \
+ partition.cpp \
+ partitionmanager.cpp \
+ progresstracking.cpp \
+ startupArgs.cpp \
+ twrp-functions.cpp \
+ twrpDigestDriver.cpp \
+ openrecoveryscript.cpp \
+ tarWrite.c \
+ twrpAdbBuFifo.cpp \
+ twrpApex.cpp \
+ twrpRepacker.cpp
+
+LOCAL_STATIC_LIBRARIES += libavb libtwrpinstall libminadbd_services libinit
+LOCAL_SHARED_LIBRARIES += libfs_mgr
+LOCAL_C_INCLUDES += \
+ system/core/fs_mgr/libfs_avb/include/ \
+ system/core/fs_mgr/include_fstab/ \
+ system/core/fs_mgr/include/ \
+ system/core/fs_mgr/libdm/include/ \
+ system/core/fs_mgr/liblp/include/ \
+ system/gsid/include/ \
+ system/core/init/ \
+ system/extras/ext4_utils/include \
+ $(LOCAL_PATH)/twinstall/include
+
+ifneq ($(TARGET_RECOVERY_REBOOT_SRC),)
+ LOCAL_SRC_FILES += $(TARGET_RECOVERY_REBOOT_SRC)
+endif
+
+LOCAL_MODULE := recovery
+
RECOVERY_API_VERSION := 3
RECOVERY_FSTAB_VERSION := 2
+LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION)
+LOCAL_CFLAGS += -Wno-unused-parameter -Wno-unused-function
+LOCAL_CLANG := true
-# TARGET_RECOVERY_UI_LIB should be one of librecovery_ui_{default,wear,vr,ethernet} or a
-# device-specific module that defines make_device() and the exact RecoveryUI class for the
-# target. It defaults to librecovery_ui_default, which uses ScreenRecoveryUI.
-TARGET_RECOVERY_UI_LIB ?= librecovery_ui_default
+LOCAL_C_INCLUDES += \
+ bionic \
+ system/vold \
+ system/extras \
+ system/core/adb \
+ system/core/libsparse \
+ external/zlib \
+ system/core/libpixelflinger/include \
+ system/core/libziparchive/include \
+ external/freetype/include \
+ external/boringssl/include \
+ external/libcxx/include \
+ external/libselinux/include \
+ $(LOCAL_PATH)/recovery_ui/include \
+ $(LOCAL_PATH)/otautil/include \
+ $(LOCAL_PATH)/install/include \
+ $(LOCAL_PATH)/fuse_sideload/include \
+ $(LOCAL_PATH)/install/include \
+ $(LOCAL_PATH)/twrpinstall/include \
+ $(LOCAL_PATH)/recovery_utils/include
-# librecovery_ui_ext (shared library)
-# ===================================
-include $(CLEAR_VARS)
+LOCAL_STATIC_LIBRARIES += libguitwrp
+LOCAL_SHARED_LIBRARIES += libz libc libcutils libstdc++ libtar libblkid libminuitwrp libmtdutils libtwadbbu
+LOCAL_SHARED_LIBRARIES += libbootloader_message libcrecovery libtwrpdigest libc++ libaosprecovery libcrypto libbase
+LOCAL_SHARED_LIBRARIES += libziparchive libselinux
-LOCAL_MODULE := librecovery_ui_ext
+ifneq ($(wildcard system/core/libsparse/Android.mk),)
+LOCAL_SHARED_LIBRARIES += libsparse
+endif
-# LOCAL_MODULE_PATH for shared libraries is unsupported in multiarch builds.
-LOCAL_MULTILIB := first
+ifeq ($(TW_OEM_BUILD),true)
+ LOCAL_CFLAGS += -DTW_OEM_BUILD
+ BOARD_HAS_NO_REAL_SDCARD := true
+ TW_USE_TOOLBOX := true
+ TW_EXCLUDE_MTP := true
+endif
-ifeq ($(TARGET_IS_64_BIT),true)
-LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/system/lib64
+ifeq ($(AB_OTA_UPDATER),true)
+ LOCAL_CFLAGS += -DAB_OTA_UPDATER=1
+ LOCAL_SHARED_LIBRARIES += libhardware android.hardware.boot@1.0
+ TWRP_REQUIRED_MODULES += libhardware android.hardware.boot@1.0-service android.hardware.boot@1.0-service.rc
+endif
+
+ifeq ($(PRODUCT_USE_DYNAMIC_PARTITIONS),true)
+ LOCAL_CFLAGS += -DPRODUCT_USE_DYNAMIC_PARTITIONS=1
+ TWRP_REQUIRED_MODULES += android.hardware.health@2.0-service android.hardware.health@2.0-service.rc
+endif
+
+ifeq ($(TW_USES_VENDOR_LIBS),true)
+ LOCAL_CFLAGS += -DUSE_VENDOR_LIBS=1
+endif
+
+ifneq ($(BOARD_GOOGLE_DYNAMIC_PARTITIONS_PARTITION_LIST),)
+ BOARD_DYNAMIC_PARTITIONS_PARTITION_LIST := $(BOARD_GOOGLE_DYNAMIC_PARTITIONS_PARTITION_LIST)
+else ifneq ($(BOARD_QTI_DYNAMIC_PARTITIONS_PARTITION_LIST),)
+ BOARD_DYNAMIC_PARTITIONS_PARTITION_LIST := $(BOARD_QTI_DYNAMIC_PARTITIONS_PARTITION_LIST)
+else ifneq ($(BOARD_SAMSUNG_DYNAMIC_PARTITIONS_PARTITION_LIST),)
+ BOARD_DYNAMIC_PARTITIONS_PARTITION_LIST := $(BOARD_SAMSUNG_DYNAMIC_PARTITIONS_PARTITION_LIST)
+endif
+ifneq ($(BOARD_DYNAMIC_PARTITIONS_PARTITION_LIST),)
+ LOCAL_CFLAGS += "-DBOARD_DYNAMIC_PARTITIONS_PARTITION_LIST=\"$(shell echo $(BOARD_DYNAMIC_PARTITIONS_PARTITION_LIST) | sed -r 's/\b(.)/\u\1/g' | sed -e 's/ \+/, /g')\""
+endif
+
+ifeq ($(TW_NO_BIND_SYSTEM),true)
+ LOCAL_CFLAGS += -DTW_NO_BIND_SYSTEM
+endif
+
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/system/bin
+
+ifeq ($(TARGET_RECOVERY_TWRP_LIB),)
+ LOCAL_SRC_FILES += BasePartition.cpp
else
-LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/system/lib
+ LOCAL_STATIC_LIBRARIES += $(TARGET_RECOVERY_TWRP_LIB)
endif
-LOCAL_WHOLE_STATIC_LIBRARIES := \
- $(TARGET_RECOVERY_UI_LIB)
+LOCAL_C_INCLUDES += system/extras/ext4_utils
-LOCAL_SHARED_LIBRARIES := \
- libbase \
- liblog \
- librecovery_ui.recovery
+tw_git_revision := $(shell git -C $(LOCAL_PATH) rev-parse --short=8 HEAD 2>/dev/null)
+ifeq ($(shell git -C $(LOCAL_PATH) diff --quiet; echo $$?),1)
+ tw_git_revision := $(tw_git_revision)-dirty
+endif
+LOCAL_CFLAGS += -DTW_GIT_REVISION='"$(tw_git_revision)"'
-include $(BUILD_SHARED_LIBRARY)
+ifeq ($(TW_FORCE_USE_BUSYBOX), true)
+ TW_USE_TOOLBOX := false
+else
+ TW_USE_TOOLBOX := true
+endif
+ifeq ($(TW_EXCLUDE_MTP),)
+ LOCAL_SHARED_LIBRARIES += libtwrpmtp-ffs
+endif
-# recovery_deps: A phony target that's depended on by `recovery`, which
-# builds additional modules conditionally based on Makefile variables.
-# ======================================================================
+#TWRP Build Flags
+ifeq ($(TW_EXCLUDE_MTP),)
+ LOCAL_CFLAGS += -DTW_HAS_MTP
+endif
+ifneq ($(TW_NO_SCREEN_TIMEOUT),)
+ LOCAL_CFLAGS += -DTW_NO_SCREEN_TIMEOUT
+endif
+ifeq ($(BOARD_HAS_NO_REAL_SDCARD), true)
+ LOCAL_CFLAGS += -DBOARD_HAS_NO_REAL_SDCARD
+endif
+ifneq ($(RECOVERY_SDCARD_ON_DATA),)
+ LOCAL_CFLAGS += -DRECOVERY_SDCARD_ON_DATA
+endif
+ifneq ($(TW_INCLUDE_DUMLOCK),)
+ LOCAL_CFLAGS += -DTW_INCLUDE_DUMLOCK
+endif
+ifneq ($(TW_INTERNAL_STORAGE_PATH),)
+ LOCAL_CFLAGS += -DTW_INTERNAL_STORAGE_PATH=$(TW_INTERNAL_STORAGE_PATH)
+endif
+ifneq ($(TW_INTERNAL_STORAGE_MOUNT_POINT),)
+ LOCAL_CFLAGS += -DTW_INTERNAL_STORAGE_MOUNT_POINT=$(TW_INTERNAL_STORAGE_MOUNT_POINT)
+endif
+ifneq ($(TW_EXTERNAL_STORAGE_PATH),)
+ LOCAL_CFLAGS += -DTW_EXTERNAL_STORAGE_PATH=$(TW_EXTERNAL_STORAGE_PATH)
+endif
+ifneq ($(TW_EXTERNAL_STORAGE_MOUNT_POINT),)
+ LOCAL_CFLAGS += -DTW_EXTERNAL_STORAGE_MOUNT_POINT=$(TW_EXTERNAL_STORAGE_MOUNT_POINT)
+endif
+ifeq ($(TW_HAS_NO_BOOT_PARTITION), true)
+ LOCAL_CFLAGS += -DTW_HAS_NO_BOOT_PARTITION
+endif
+ifeq ($(TW_NO_REBOOT_BOOTLOADER), true)
+ LOCAL_CFLAGS += -DTW_NO_REBOOT_BOOTLOADER
+endif
+ifeq ($(TW_NO_REBOOT_RECOVERY), true)
+ LOCAL_CFLAGS += -DTW_NO_REBOOT_RECOVERY
+endif
+ifeq ($(TW_NO_BATT_PERCENT), true)
+ LOCAL_CFLAGS += -DTW_NO_BATT_PERCENT
+endif
+ifeq ($(TW_NO_CPU_TEMP), true)
+ LOCAL_CFLAGS += -DTW_NO_CPU_TEMP
+endif
+ifneq ($(TW_CUSTOM_POWER_BUTTON),)
+ LOCAL_CFLAGS += -DTW_CUSTOM_POWER_BUTTON=$(TW_CUSTOM_POWER_BUTTON)
+endif
+ifeq ($(TW_ALWAYS_RMRF), true)
+ LOCAL_CFLAGS += -DTW_ALWAYS_RMRF
+endif
+ifeq ($(TW_NEVER_UNMOUNT_SYSTEM), true)
+ LOCAL_CFLAGS += -DTW_NEVER_UNMOUNT_SYSTEM
+endif
+ifeq ($(TW_NO_USB_STORAGE), true)
+ LOCAL_CFLAGS += -DTW_NO_USB_STORAGE
+endif
+ifeq ($(TW_INCLUDE_INJECTTWRP), true)
+ LOCAL_CFLAGS += -DTW_INCLUDE_INJECTTWRP
+endif
+ifeq ($(TW_INCLUDE_BLOBPACK), true)
+ LOCAL_CFLAGS += -DTW_INCLUDE_BLOBPACK
+endif
+ifneq ($(TARGET_USE_CUSTOM_LUN_FILE_PATH),)
+ LOCAL_CFLAGS += -DCUSTOM_LUN_FILE=\"$(TARGET_USE_CUSTOM_LUN_FILE_PATH)\"
+endif
+ifneq ($(BOARD_UMS_LUNFILE),)
+ LOCAL_CFLAGS += -DCUSTOM_LUN_FILE=\"$(BOARD_UMS_LUNFILE)\"
+endif
+ifeq ($(TW_HAS_DOWNLOAD_MODE), true)
+ LOCAL_CFLAGS += -DTW_HAS_DOWNLOAD_MODE
+endif
+ifeq ($(TW_HAS_EDL_MODE), true)
+ LOCAL_CFLAGS += -DTW_HAS_EDL_MODE
+endif
+ifeq ($(TW_NO_SCREEN_BLANK), true)
+ LOCAL_CFLAGS += -DTW_NO_SCREEN_BLANK
+endif
+ifeq ($(TW_SDEXT_NO_EXT4), true)
+ LOCAL_CFLAGS += -DTW_SDEXT_NO_EXT4
+endif
+ifeq ($(TW_FORCE_CPUINFO_FOR_DEVICE_ID), true)
+ LOCAL_CFLAGS += -DTW_FORCE_CPUINFO_FOR_DEVICE_ID
+endif
+ifeq ($(TW_NO_EXFAT_FUSE), true)
+ LOCAL_CFLAGS += -DTW_NO_EXFAT_FUSE
+endif
+ifeq ($(TW_NO_HAPTICS), true)
+ LOCAL_CFLAGS += -DTW_NO_HAPTICS
+endif
+ifeq ($(TW_INCLUDE_JB_CRYPTO), true)
+ TW_INCLUDE_CRYPTO := true
+endif
+ifeq ($(TW_INCLUDE_L_CRYPTO), true)
+ TW_INCLUDE_CRYPTO := true
+endif
+ifeq ($(TW_INCLUDE_CRYPTO), true)
+ LOCAL_CFLAGS += -DTW_INCLUDE_CRYPTO -DUSE_FSCRYPT -Wno-macro-redefined
+ # LOCAL_SHARED_LIBRARIES += libcryptfsfde
+ LOCAL_SHARED_LIBRARIES += libgpt_twrp
+ LOCAL_C_INCLUDES += external/boringssl/src/include bootable/recovery/crypto/fscrypt \
+ bootable/recovery/crypto
+ TW_INCLUDE_CRYPTO_FBE := true
+ LOCAL_CFLAGS += -DTW_INCLUDE_FBE
+ LOCAL_SHARED_LIBRARIES += libtwrpfscrypt android.frameworks.stats@1.0 android.hardware.authsecret@1.0 \
+ android.hardware.oemlock@1.0
+ LOCAL_CFLAGS += -DTW_INCLUDE_FBE_METADATA_DECRYPT
+ ifneq ($(TW_CRYPTO_USE_SYSTEM_VOLD),)
+ ifneq ($(TW_CRYPTO_USE_SYSTEM_VOLD),false)
+ TW_INCLUDE_LIBRESETPROP := true
+ LOCAL_CFLAGS += -DTW_CRYPTO_USE_SYSTEM_VOLD
+ LOCAL_STATIC_LIBRARIES += libvolddecrypt
+ endif
+ endif
+endif
+WITH_CRYPTO_UTILS := \
+ $(if $(wildcard system/core/libcrypto_utils/android_pubkey.c),true)
+ifeq ($(TW_USE_MODEL_HARDWARE_ID_FOR_DEVICE_ID), true)
+ LOCAL_CFLAGS += -DTW_USE_MODEL_HARDWARE_ID_FOR_DEVICE_ID
+endif
+ifeq ($(TW_USE_SERIALNO_PROPERTY_FOR_DEVICE_ID), true)
+ LOCAL_CFLAGS += -DTW_USE_SERIALNO_PROPERTY_FOR_DEVICE_ID
+endif
+ifneq ($(TW_BRIGHTNESS_PATH),)
+ LOCAL_CFLAGS += -DTW_BRIGHTNESS_PATH=$(TW_BRIGHTNESS_PATH)
+endif
+ifneq ($(TW_SECONDARY_BRIGHTNESS_PATH),)
+ LOCAL_CFLAGS += -DTW_SECONDARY_BRIGHTNESS_PATH=$(TW_SECONDARY_BRIGHTNESS_PATH)
+endif
+ifneq ($(TW_MAX_BRIGHTNESS),)
+ LOCAL_CFLAGS += -DTW_MAX_BRIGHTNESS=$(TW_MAX_BRIGHTNESS)
+endif
+ifneq ($(TW_DEFAULT_BRIGHTNESS),)
+ LOCAL_CFLAGS += -DTW_DEFAULT_BRIGHTNESS=$(TW_DEFAULT_BRIGHTNESS)
+endif
+ifneq ($(TW_CUSTOM_BATTERY_PATH),)
+ LOCAL_CFLAGS += -DTW_CUSTOM_BATTERY_PATH=$(TW_CUSTOM_BATTERY_PATH)
+endif
+ifneq ($(TW_CUSTOM_CPU_TEMP_PATH),)
+ LOCAL_CFLAGS += -DTW_CUSTOM_CPU_TEMP_PATH=$(TW_CUSTOM_CPU_TEMP_PATH)
+endif
+ifneq ($(TW_EXCLUDE_ENCRYPTED_BACKUPS), true)
+ LOCAL_SHARED_LIBRARIES += libopenaes
+else
+ LOCAL_CFLAGS += -DTW_EXCLUDE_ENCRYPTED_BACKUPS
+endif
+ifeq ($(TARGET_RECOVERY_QCOM_RTC_FIX),)
+ ifneq ($(filter msm8226 msm8x26 msm8610 msm8974 msm8x74 msm8084 msm8x84 apq8084 msm8909 msm8916 msm8992 msm8994 msm8952 msm8996 msm8937 msm8953 msm8998,$(TARGET_BOARD_PLATFORM)),)
+ LOCAL_CFLAGS += -DQCOM_RTC_FIX
+ else ifeq ($(TARGET_CPU_VARIANT),krait)
+ LOCAL_CFLAGS += -DQCOM_RTC_FIX
+ endif
+else ifeq ($(TARGET_RECOVERY_QCOM_RTC_FIX),true)
+ LOCAL_CFLAGS += -DQCOM_RTC_FIX
+endif
+ifneq ($(TW_NO_LEGACY_PROPS),)
+ LOCAL_CFLAGS += -DTW_NO_LEGACY_PROPS
+endif
+ifneq ($(wildcard bionic/libc/include/sys/capability.h),)
+ LOCAL_CFLAGS += -DHAVE_CAPABILITIES
+endif
+ifneq ($(TARGET_RECOVERY_INITRC),)
+ TW_EXCLUDE_DEFAULT_USB_INIT := true
+endif
+LOCAL_CFLAGS += -DTW_USE_NEW_MINADBD
+ifneq ($(TW_DEFAULT_LANGUAGE),)
+ LOCAL_CFLAGS += -DTW_DEFAULT_LANGUAGE=$(TW_DEFAULT_LANGUAGE)
+else
+ LOCAL_CFLAGS += -DTW_DEFAULT_LANGUAGE=en
+endif
+ifneq ($(TW_CLOCK_OFFSET),)
+ LOCAL_CFLAGS += -DTW_CLOCK_OFFSET=$(TW_CLOCK_OFFSET)
+endif
+ifneq ($(TW_OVERRIDE_SYSTEM_PROPS),)
+ TW_INCLUDE_LIBRESETPROP := true
+ LOCAL_CFLAGS += -DTW_OVERRIDE_SYSTEM_PROPS=$(TW_OVERRIDE_SYSTEM_PROPS)
+endif
+ifneq ($(TW_INCLUDE_LIBRESETPROP),)
+ LOCAL_SHARED_LIBRARIES += libresetprop
+ LOCAL_C_INCLUDES += external/magisk-prebuilt/include
+ LOCAL_CFLAGS += -DTW_INCLUDE_LIBRESETPROP
+endif
+
+TWRP_REQUIRED_MODULES += \
+ relink_libraries \
+ relink_binaries \
+ twrp_ramdisk \
+ dump_image \
+ erase_image \
+ flash_image \
+ mke2fs.conf \
+ pigz \
+ teamwin \
+ twrp \
+ fsck.fat \
+ fatlabel \
+ mkfs.fat \
+ permissive.sh \
+ simg2img_twrp \
+ libbootloader_message \
+ init.recovery.hlthchrg.rc \
+ init.recovery.service.rc \
+ init.recovery.ldconfig.rc \
+ awk \
+ toybox \
+ toolbox \
+ mkshrc_twrp \
+ plat_hwservice_contexts \
+ vendor_hwservice_contexts \
+ minadbd \
+ twrpbu
+
+ifneq ($(TW_INCLUDE_CRYPTO),)
+TWRP_REQUIRED_MODULES += \
+ hwservicemanager \
+ hwservicemanager.rc \
+ vndservicemanager \
+ vndservicemanager.rc \
+ vold_prepare_subdirs \
+ task_recovery_profiles.json \
+ fscryptpolicyget
+ ifneq ($(TW_INCLUDE_CRYPTO_FBE),)
+ TWRP_REQUIRED_MODULES += \
+ plat_service_contexts \
+ servicemanager \
+ servicemanager.rc
+ endif
+endif
+
+ifneq ($(wildcard external/zip/Android.mk),)
+ TWRP_REQUIRED_MODULES += zip
+endif
+ifneq ($(wildcard external/unzip/Android.mk),)
+ TWRP_REQUIRED_MODULES += unzip
+endif
+
+ifneq ($(TW_NO_EXFAT), true)
+ TWRP_REQUIRED_MODULES += mkexfatfs fsckexfat
+ ifneq ($(TW_NO_EXFAT_FUSE), true)
+ TWRP_REQUIRED_MODULES += exfat-fuse
+ endif
+endif
+ifeq ($(BOARD_HAS_NO_REAL_SDCARD),)
+ TWRP_REQUIRED_MODULES += sgdisk
+endif
+ifneq ($(TW_EXCLUDE_ENCRYPTED_BACKUPS), true)
+ TWRP_REQUIRED_MODULES += openaes openaes_license
+endif
+ifeq ($(TW_INCLUDE_DUMLOCK), true)
+ TWRP_REQUIRED_MODULES += \
+ htcdumlock htcdumlocksys flash_imagesys dump_imagesys libbmlutils.so \
+ libflashutils.so libmmcutils.so libmtdutils.so HTCDumlock.apk
+endif
+ifeq ($(TW_INCLUDE_FB2PNG), true)
+ TWRP_REQUIRED_MODULES += fb2png
+endif
+ifneq ($(TW_OEM_BUILD),true)
+ TWRP_REQUIRED_MODULES += orscmd
+endif
+ifeq ($(BOARD_USES_BML_OVER_MTD),true)
+ TWRP_REQUIRED_MODULES += bml_over_mtd
+endif
+ifeq ($(TW_INCLUDE_INJECTTWRP), true)
+ TWRP_REQUIRED_MODULES += injecttwrp
+endif
+ifneq ($(TW_EXCLUDE_DEFAULT_USB_INIT), true)
+ TWRP_REQUIRED_MODULES += init.recovery.usb.rc
+endif
+ifeq ($(TWRP_INCLUDE_LOGCAT), true)
+ TWRP_REQUIRED_MODULES += logcat event-log-tags
+ ifeq ($(TARGET_USES_LOGD), true)
+ TWRP_REQUIRED_MODULES += logd libsysutils libnl init.recovery.logd.rc
+ endif
+endif
+# Allow devices to specify device-specific recovery dependencies
+ifneq ($(TARGET_RECOVERY_DEVICE_MODULES),)
+ TWRP_REQUIRED_MODULES += $(TARGET_RECOVERY_DEVICE_MODULES)
+endif
+LOCAL_CFLAGS += -DTWRES=\"$(TWRES_PATH)\"
+LOCAL_CFLAGS += -DTWHTCD_PATH=\"$(TWHTCD_PATH)\"
+ifeq ($(TW_INCLUDE_NTFS_3G),true)
+ TWRP_REQUIRED_MODULES += \
+ mount.ntfs \
+ fsck.ntfs \
+ mkfs.ntfs
+endif
+ifeq ($(TARGET_USERIMAGES_USE_F2FS), true)
+ TWRP_REQUIRED_MODULES += sload.f2fs \
+ libfs_mgr \
+ fs_mgr \
+ libinit
+endif
+
+TWRP_REQUIRED_MODULES += file_contexts_text
+
+ifeq ($(BOARD_CACHEIMAGE_PARTITION_SIZE),)
+ TWRP_REQUIRED_MODULES += recovery-persist recovery-refresh
+endif
+
+LOCAL_REQUIRED_MODULES += $(TWRP_REQUIRED_MODULES)
+
+include $(BUILD_EXECUTABLE)
+
+# Symlink for file_contexts
include $(CLEAR_VARS)
-LOCAL_MODULE := recovery_deps
+LOCAL_MODULE := file_contexts_text
+LOCAL_MODULE_TAGS := optional
+LOCAL_REQUIRED_MODULES := file_contexts.bin
-ifeq ($(TARGET_USERIMAGES_USE_F2FS),true)
-ifeq ($(HOST_OS),linux)
-LOCAL_REQUIRED_MODULES += \
- make_f2fs.recovery \
- sload_f2fs.recovery
-endif
-endif
-
-# On A/B devices recovery-persist reads the recovery related file from the persist storage and
-# copies them into /data/misc/recovery. Then, for both A/B and non-A/B devices, recovery-persist
-# parses the last_install file and reports the embedded update metrics. Also, the last_install file
-# will be deteleted after the report.
-LOCAL_REQUIRED_MODULES += recovery-persist
-ifeq ($(BOARD_CACHEIMAGE_PARTITION_SIZE),)
-LOCAL_REQUIRED_MODULES += recovery-refresh
-endif
+LOCAL_POST_INSTALL_CMD := \
+ $(hide) cp -f $(PRODUCT_OUT)/obj/ETC/file_contexts.bin_intermediates/file_contexts.concat.tmp $(TARGET_RECOVERY_ROOT_OUT)/file_contexts
include $(BUILD_PHONY_PACKAGE)
+# recovery-persist (system partition dynamic executable run after /data mounts)
+# ===============================
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := \
+ recovery-persist.cpp
+LOCAL_MODULE := recovery-persist
+LOCAL_SHARED_LIBRARIES := liblog libbase
+LOCAL_STATIC_LIBRARIES := libotautil librecovery_utils
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/otautil/include
+LOCAL_C_INCLUDES += system/core/libstats/include
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/recovery_utils/include
+LOCAL_CFLAGS := -Werror
+LOCAL_INIT_RC := recovery-persist.rc
+include $(BUILD_EXECUTABLE)
+
+# recovery-refresh (system partition dynamic executable run at init)
+# ===============================
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := \
+ recovery-refresh.cpp
+LOCAL_MODULE := recovery-refresh
+LOCAL_SHARED_LIBRARIES := liblog libbase
+LOCAL_STATIC_LIBRARIES := libotautil librecovery_utils
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/otautil/include
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/recovery_utils/include
+LOCAL_CFLAGS := -Werror
+LOCAL_INIT_RC := recovery-refresh.rc
+include $(BUILD_EXECUTABLE)
+
+# libmounts (static library)
+# ===============================
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := mounts.cpp
+LOCAL_CFLAGS := \
+ -Wall \
+ -Werror
+LOCAL_MODULE := libmounts
+LOCAL_STATIC_LIBRARIES := libbase
+include $(BUILD_STATIC_LIBRARY)
+
+# librecovery (static library)
+# ===============================
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := \
+ install.cpp
+LOCAL_CFLAGS := -Wall -Werror
+LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION)
+
+ifeq ($(AB_OTA_UPDATER),true)
+ LOCAL_CFLAGS += -DAB_OTA_UPDATER=1
+endif
+
+LOCAL_MODULE := librecovery
+LOCAL_STATIC_LIBRARIES := \
+ libminui \
+ libotautil \
+ libvintf \
+ libcrypto_utils \
+ libcrypto \
+ libbase \
+ libziparchive \
+
+include $(BUILD_STATIC_LIBRARY)
+
+commands_recovery_local_path := $(LOCAL_PATH)
+
include \
- $(LOCAL_PATH)/updater/Android.mk \
+ $(commands_TWRP_local_path)/updater/Android.mk
+
+include $(commands_TWRP_local_path)/mtp/ffs/Android.mk \
+ $(commands_TWRP_local_path)/minui/Android.mk
+
+#includes for TWRP
+include $(commands_TWRP_local_path)/injecttwrp/Android.mk \
+ $(commands_TWRP_local_path)/htcdumlock/Android.mk \
+ $(commands_TWRP_local_path)/mmcutils/Android.mk \
+ $(commands_TWRP_local_path)/bmlutils/Android.mk \
+ $(commands_TWRP_local_path)/prebuilt/Android.mk \
+ $(commands_TWRP_local_path)/mtdutils/Android.mk \
+ $(commands_TWRP_local_path)/flashutils/Android.mk \
+ $(commands_TWRP_local_path)/pigz/Android.mk \
+ $(commands_TWRP_local_path)/libtar/Android.mk \
+ $(commands_TWRP_local_path)/libcrecovery/Android.mk \
+ $(commands_TWRP_local_path)/libblkid/Android.mk \
+ $(commands_TWRP_local_path)/openaes/Android.mk \
+ $(commands_TWRP_local_path)/twrpTarMain/Android.mk \
+ $(commands_TWRP_local_path)/minzip/Android.mk \
+ $(commands_TWRP_local_path)/dosfstools/Android.mk \
+ $(commands_TWRP_local_path)/etc/Android.mk \
+ $(commands_TWRP_local_path)/simg2img/Android.mk \
+ $(commands_TWRP_local_path)/adbbu/Android.mk \
+ $(commands_TWRP_local_path)/twrpDigest/Android.mk \
+ $(commands_TWRP_local_path)/attr/Android.mk
+
+ifneq ($(TW_OZIP_DECRYPT_KEY),)
+ TWRP_REQUIRED_MODULES += ozip_decrypt
+ include $(commands_TWRP_local_path)/ozip_decrypt/Android.mk
+endif
+
+ifeq ($(TW_INCLUDE_CRYPTO), true)
+ # include $(commands_TWRP_local_path)/crypto/fde/Android.mk
+ include $(commands_TWRP_local_path)/crypto/scrypt/Android.mk
+ ifeq ($(TW_INCLUDE_CRYPTO_FBE), true)
+ include $(commands_TWRP_local_path)/crypto/fscrypt/Android.mk
+ endif
+ ifneq ($(TW_CRYPTO_USE_SYSTEM_VOLD),)
+ ifneq ($(TW_CRYPTO_USE_SYSTEM_VOLD),false)
+ include $(commands_TWRP_local_path)/crypto/vold_decrypt/Android.mk
+ endif
+ endif
+ include $(commands_TWRP_local_path)/gpt/Android.mk
+endif
+ifeq ($(BUILD_ID), GINGERBREAD)
+ TW_NO_EXFAT := true
+endif
+ifneq ($(TW_NO_EXFAT), true)
+ include $(commands_TWRP_local_path)/exfat/mkfs/Android.mk \
+ $(commands_TWRP_local_path)/exfat/fsck/Android.mk \
+ $(commands_TWRP_local_path)/fuse/Android.mk \
+ $(commands_TWRP_local_path)/exfat/libexfat/Android.mk
+ ifneq ($(TW_NO_EXFAT_FUSE), true)
+ include $(commands_TWRP_local_path)/exfat/fuse/Android.mk
+ endif
+endif
+ifneq ($(TW_OEM_BUILD),true)
+ include $(commands_TWRP_local_path)/orscmd/Android.mk
+endif
+
+# FB2PNG
+ifeq ($(TW_INCLUDE_FB2PNG), true)
+ include $(commands_TWRP_local_path)/fb2png/Android.mk
+endif
+
+endif
+
+commands_TWRP_local_path :=
diff --git a/BasePartition.cpp b/BasePartition.cpp
new file mode 100644
index 0000000..3b34d75
--- /dev/null
+++ b/BasePartition.cpp
@@ -0,0 +1,28 @@
+/*
+ Copyright 2018 TeamWin
+ This file is part of TWRP/TeamWin Recovery Project.
+
+ TWRP is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ TWRP is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with TWRP. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#include "partitions.hpp"
+
+// On devices that need it, this is supposed to be overridden using
+// TARGET_RECOVERY_TWRP_LIB to allow device-specific pre and post
+// wipe encryption calls.
+
+BasePartition* make_partition() {
+ return new BasePartition();
+}
diff --git a/NOTICE b/NOTICE
index c5b1efa..346e71a 100644
--- a/NOTICE
+++ b/NOTICE
@@ -1,190 +1,634 @@
- Copyright (c) 2005-2008, The Android Open Source Project
+ Copyright (c) 2011-2016, Dees_Troy, bigbiff, Team Win
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+ Preamble
- 1. Definitions.
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
+ The precise terms and conditions for copying, distribution and
+modification follow.
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
+ TERMS AND CONDITIONS
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
+ 0. Definitions.
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
+ "This License" refers to version 3 of the GNU General Public License.
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
+ 1. Source Code.
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
- END OF TERMS AND CONDITIONS
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
diff --git a/README.md b/README.md
old mode 100644
new mode 100755
index bd1cf7d..820a8b7
--- a/README.md
+++ b/README.md
@@ -1,145 +1,3 @@
-The Recovery Image
-==================
+**Team Win Recovery Project (TWRP)**
-Quick turn-around testing
--------------------------
-
-* Devices using recovery-as-boot (e.g. Pixels, which set BOARD\_USES\_RECOVERY\_AS\_BOOT)
-
- # After setting up environment and lunch.
- m -j bootimage
- adb reboot bootloader
-
- # Pixel devices don't support booting into recovery mode with `fastboot boot`.
- fastboot flash boot
-
- # Manually choose `Recovery mode` from bootloader menu.
-
-* Devices with a separate recovery image (e.g. Nexus)
-
- # After setting up environment and lunch.
- mm -j && m ramdisk-nodeps && m recoveryimage-nodeps
- adb reboot bootloader
-
- # To boot into the new recovery image without flashing the recovery partition:
- fastboot boot $ANDROID_PRODUCT_OUT/recovery.img
-
-Running the tests
------------------
-
- # After setting up environment and lunch.
- mmma -j bootable/recovery
-
- # Running the tests on device (under normal boot).
- adb root
- adb sync data
-
- # 32-bit device
- adb shell /data/nativetest/recovery_unit_test/recovery_unit_test
-
- # Or 64-bit device
- adb shell /data/nativetest64/recovery_unit_test/recovery_unit_test
-
-Running the manual tests
-------------------------
-
-`recovery-refresh` and `recovery-persist` executables exist only on systems without
-/cache partition. And we need to follow special steps to run tests for them.
-
-- Execute the test on an A/B device first. The test should fail but it will log
- some contents to pmsg.
-
-- Reboot the device immediately and run the test again. The test should save the
- contents of pmsg buffer into /data/misc/recovery/inject.txt. Test will pass if
- this file has expected contents.
-
-Using `adb` under recovery
---------------------------
-
-When running recovery image from debuggable builds (i.e. `-eng` or `-userdebug` build variants, or
-`ro.debuggable=1` in `/prop.default`), `adbd` service is enabled and started by default, which
-allows `adb` communication. A device should be listed under `adb devices`, either in `recovery` or
-`sideload` state.
-
- $ adb devices
- List of devices attached
- 1234567890abcdef recovery
-
-Although `/system/bin/adbd` is built from the same code base as the one in the normal boot, only a
-subset of `adb` commands are meaningful under recovery, such as `adb root`, `adb shell`, `adb push`,
-`adb pull` etc. Since Android Q, `adb shell` no longer requires manually mounting `/system` from
-recovery menu.
-
-## Troubleshooting
-
-### `adb devices` doesn't show the device.
-
- $ adb devices
- List of devices attached
-
- * Ensure `adbd` is built and running.
-
-By default, `adbd` is always included into recovery image, as `/system/bin/adbd`. `init` starts
-`adbd` service automatically only in debuggable builds. This behavior is controlled by the recovery
-specific `/init.rc`, whose source code is at `bootable/recovery/etc/init.rc`.
-
-The best way to confirm a running `adbd` is by checking the serial output, which shows a service
-start log as below.
-
- [ 18.961986] c1 1 init: starting service 'adbd'...
-
- * Ensure USB gadget has been enabled.
-
-If `adbd` service has been started but device not shown under `adb devices`, use `lsusb(8)` (on
-host) to check if the device is visible to the host.
-
-`bootable/recovery/etc/init.rc` disables Android USB gadget (via sysfs) as part of the `fs` action
-trigger, and will only re-enable it in debuggable builds (the `on property` rule will always run
-_after_ `on fs`).
-
- on fs
- write /sys/class/android_usb/android0/enable 0
-
- # Always start adbd on userdebug and eng builds
- on property:ro.debuggable=1
- write /sys/class/android_usb/android0/enable 1
- start adbd
-
-If device is using [configfs](https://www.kernel.org/doc/Documentation/usb/gadget_configfs.txt),
-check if configfs has been properly set up in init rc scripts. See the [example
-configuration](https://android.googlesource.com/device/google/wahoo/+/master/init.recovery.hardware.rc)
-for Pixel 2 devices. Note that the flag set via sysfs (i.e. the one above) is no-op when using
-configfs.
-
-### `adb devices` shows the device, but in `unauthorized` state.
-
- $ adb devices
- List of devices attached
- 1234567890abcdef unauthorized
-
-recovery image doesn't honor the USB debugging toggle and the authorizations added under normal boot
-(because such authorization data stays in /data, which recovery doesn't mount), nor does it support
-authorizing a host device under recovery. We can use one of the following options instead.
-
- * **Option 1 (Recommended):** Authorize a host device with adb vendor keys.
-
-For debuggable builds, an RSA keypair can be used to authorize a host device that has the private
-key. The public key, defined via `PRODUCT_ADB_KEYS`, will be copied to `/adb_keys`. When starting
-the host-side `adbd`, make sure the filename (or the directory) of the matching private key has been
-added to `$ADB_VENDOR_KEYS`.
-
- $ export ADB_VENDOR_KEYS=/path/to/adb/private/key
- $ adb kill-server
- $ adb devices
-
-`-user` builds filter out `PRODUCT_ADB_KEYS`, so no `/adb_keys` will be included there.
-
-Note that this mechanism applies to both of normal boot and recovery modes.
-
- * **Option 2:** Allow `adbd` to connect without authentication.
- * `adbd` is compiled with `ALLOW_ADBD_NO_AUTH` (only on debuggable builds).
- * `ro.adb.secure` has a value of `0`.
-
-Both of the two conditions need to be satisfied. Although `ro.adb.secure` is a runtime property, its
-value is set at build time (written into `/prop.default`). It defaults to `1` on `-user` builds, and
-`0` for other build variants. The value is overridable via `PRODUCT_DEFAULT_PROPERTY_OVERRIDES`.
+You can find a compiling guide [here](http://forum.xda-developers.com/showthread.php?t=1943625 "Guide").
diff --git a/adbbu/Android.mk b/adbbu/Android.mk
new file mode 100755
index 0000000..c05f265
--- /dev/null
+++ b/adbbu/Android.mk
@@ -0,0 +1,47 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libtwadbbu
+LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS = -fno-strict-aliasing -D_LARGFILE_SOURCE #-D_DEBUG_ADB_BACKUP
+LOCAL_C_INCLUDES += bionic external/zlib
+ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 23; echo $$?),0)
+ LOCAL_C_INCLUDES += external/stlport/stlport
+endif
+
+LOCAL_SRC_FILES = \
+ libtwadbbu.cpp \
+ twrpback.cpp
+
+LOCAL_SHARED_LIBRARIES += libz libc libstdc++ libtwrpdigest
+
+ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 23; echo $$?),0)
+ LOCAL_SHARED_LIBRARIES += libstlport
+else
+ LOCAL_SHARED_LIBRARIES += libc++
+endif
+
+include $(BUILD_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ adbbumain.cpp
+
+LOCAL_SHARED_LIBRARIES += libstdc++ libz libtwadbbu
+
+ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 23; echo $$?),0)
+ LOCAL_C_INCLUDES += external/stlport/stlport
+ LOCAL_SHARED_LIBRARIES += libstlport
+else
+ LOCAL_SHARED_LIBRARIES += libc++
+endif
+
+LOCAL_C_INCLUDES += bionic external/zlib
+LOCAL_CFLAGS:= -c -W
+LOCAL_MODULE:= twrpbu
+LOCAL_MODULE_STEM := bu
+LOCAL_MODULE_TAGS:= optional
+LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/system/bin
+include $(BUILD_EXECUTABLE)
diff --git a/adbbu/adbbumain.cpp b/adbbu/adbbumain.cpp
new file mode 100644
index 0000000..bd96b20
--- /dev/null
+++ b/adbbu/adbbumain.cpp
@@ -0,0 +1,95 @@
+/*
+ Copyright 2013 to 2017 TeamWin
+ TWRP is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ TWRP is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with TWRP. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <ctype.h>
+#include <string>
+#include <sstream>
+#include <algorithm>
+
+#include "twrpback.hpp"
+#include "twadbstream.h"
+
+
+int main(int argc, char **argv) {
+ int index;
+ size_t pos = 0;
+ bool ret = false;
+ size_t maxpos = strlen(TWRPARG) + 2;
+ std::string command;
+ twrpback tw;
+
+ tw.adblogwrite("Starting adb backup and restore\n");
+ command = argv[1];
+ for (index = 2; index < argc; index++) {
+ command = command + " " + argv[index];
+ }
+
+ pos = command.find(TWRP_BACKUP_ARG);
+ if (pos == std::string::npos || pos > (maxpos + strlen(TWRP_BACKUP_ARG) + 1)) {
+ pos = command.find(TWRP_RESTORE_ARG);
+ }
+ if (pos == std::string::npos || pos > maxpos + strlen(TWRP_STREAM_ARG) + 1) {
+ pos = command.find(TWRP_STREAM_ARG);
+ }
+
+ tw.adblogwrite("command: " + command + "\n");
+ command.erase(0, pos);
+ command.erase(std::remove(command.begin(), command.end(), '\''), command.end());
+
+ if (command.substr(0, sizeof(TWRP_BACKUP_ARG) - 1) == TWRP_BACKUP_ARG) {
+ tw.adblogwrite("Starting adb backup\n");
+ if (isdigit(*argv[1]))
+ tw.adbd_fd = atoi(argv[1]);
+ else
+ tw.adbd_fd = 1;
+ ret = tw.backup(command);
+ }
+ else if (command.substr(0, sizeof(TWRP_RESTORE_ARG) - 1) == TWRP_RESTORE_ARG) {
+ tw.adblogwrite("Starting adb restore\n");
+ if (isdigit(*argv[1]))
+ tw.adbd_fd = atoi(argv[1]);
+ else
+ tw.adbd_fd = 0;
+ ret = tw.restore();
+ }
+ else if (command.substr(0, sizeof(TWRP_STREAM_ARG) - 1) == TWRP_STREAM_ARG) {
+ tw.setStreamFileName(argv[3]);
+ tw.threadStream();
+ ret = true;
+ }
+ if (ret)
+ tw.adblogwrite("Adb backup/restore completed\n");
+ else
+ tw.adblogwrite("Adb backup/restore failed\n");
+
+ if (unlink(TW_ADB_BU_CONTROL) < 0) {
+ std::stringstream str;
+ str << strerror(errno);
+ tw.adblogwrite("Unable to remove TW_ADB_BU_CONTROL: " + str.str());
+ }
+ unlink(TW_ADB_TWRP_CONTROL);
+ if (ret)
+ return 0;
+ else
+ return -1;
+}
diff --git a/adbbu/libtwadbbu.cpp b/adbbu/libtwadbbu.cpp
new file mode 100644
index 0000000..39803b0
--- /dev/null
+++ b/adbbu/libtwadbbu.cpp
@@ -0,0 +1,307 @@
+/*
+ Copyright 2013 to 2017 TeamWin
+ TWRP is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ TWRP is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with TWRP. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <zlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/select.h>
+#include <sys/time.h>
+#include <string>
+#include <vector>
+#include <fstream>
+#include <sstream>
+#include <assert.h>
+
+#include "twadbstream.h"
+#include "libtwadbbu.hpp"
+#include "twrpback.hpp"
+
+bool twadbbu::Check_ADB_Backup_File(std::string fname) {
+ struct AdbBackupStreamHeader adbbuhdr;
+ uint32_t crc, adbbuhdrcrc;
+ unsigned char buf[MAX_ADB_READ];
+ int bytes;
+
+ int fd = open(fname.c_str(), O_RDONLY);
+ if (fd < 0) {
+ printf("Unable to open %s for reading: %s.\n", fname.c_str(), strerror(errno));
+ close(fd);
+ return false;
+ }
+ bytes = read(fd, &buf, sizeof(buf));
+ close(fd);
+
+ if (memcpy(&adbbuhdr, buf, sizeof(adbbuhdr)) == NULL) {
+ printf("Unable to memcpy: %s (%s).\n", fname.c_str(), strerror(errno));
+ return false;
+ }
+ adbbuhdrcrc = adbbuhdr.crc;
+ memset(&adbbuhdr.crc, 0, sizeof(adbbuhdr.crc));
+ crc = crc32(0L, Z_NULL, 0);
+ crc = crc32(crc, (const unsigned char*) &adbbuhdr, sizeof(adbbuhdr));
+
+ return (crc == adbbuhdrcrc);
+}
+
+std::vector<std::string> twadbbu::Get_ADB_Backup_Files(std::string fname) {
+ unsigned char buf[MAX_ADB_READ];
+ struct AdbBackupControlType structcmd;
+ std::vector<std::string> adb_partitions;
+
+ int fd = open(fname.c_str(), O_RDONLY);
+ if (fd < 0) {
+ printf("Unable to open %s for reading: %s\n", fname.c_str(), strerror(errno));
+ close(fd);
+ return std::vector<std::string>();
+ }
+
+ while (true) {
+ std::string cmdstr;
+ int readbytes;
+ if ((readbytes = read(fd, &buf, sizeof(buf))) > 0) {
+ memcpy(&structcmd, buf, sizeof(structcmd));
+ assert(structcmd.type == TWENDADB || structcmd.type == TWIMG || structcmd.type == TWFN);
+ cmdstr = structcmd.type;
+ std::string cmdtype = cmdstr.substr(0, sizeof(structcmd.type) - 1);
+ if (cmdtype == TWENDADB) {
+ struct AdbBackupControlType endadb;
+ uint32_t crc, endadbcrc;
+
+ memcpy(&endadb, buf, sizeof(endadb));
+ endadbcrc = endadb.crc;
+ memset(&endadb.crc, 0, sizeof(endadb.crc));
+ crc = crc32(0L, Z_NULL, 0);
+ crc = crc32(crc, (const unsigned char*) &endadb, sizeof(endadb));
+
+ if (crc == endadbcrc) {
+ break;
+ }
+ else {
+ printf("ADB TWENDADB crc header doesn't match\n");
+ close(fd);
+ return std::vector<std::string>();
+ }
+ }
+ else if (cmdtype == TWIMG || cmdtype == TWFN) {
+ struct twfilehdr twfilehdr;
+ uint32_t crc, twfilehdrcrc;
+
+ memcpy(&twfilehdr, buf, sizeof(twfilehdr));
+ twfilehdrcrc = twfilehdr.crc;
+ memset(&twfilehdr.crc, 0, sizeof(twfilehdr.crc));
+
+ crc = crc32(0L, Z_NULL, 0);
+ crc = crc32(crc, (const unsigned char*) &twfilehdr, sizeof(twfilehdr));
+ if (crc == twfilehdrcrc) {
+ std::string adbfile = twfilehdr.name;
+ int pos = adbfile.find_last_of("/") + 1;
+ adbfile = adbfile.substr(pos, adbfile.size());
+ adb_partitions.push_back(adbfile);
+ }
+ else {
+ printf("ADB crc header doesn't match\n");
+ close(fd);
+ return std::vector<std::string>();
+ }
+ }
+ }
+ }
+ close(fd);
+ return adb_partitions;
+}
+
+bool twadbbu::Write_ADB_Stream_Header(uint64_t partition_count) {
+ struct AdbBackupStreamHeader twhdr;
+ int adb_control_bu_fd;
+
+ memset(&twhdr, 0, sizeof(twhdr));
+ adb_control_bu_fd = open(TW_ADB_BU_CONTROL, O_WRONLY | O_NONBLOCK);
+ if (adb_control_bu_fd < 0) {
+ printf("Cannot write to TW_ADB_BU_CONTROL: %s\n", strerror(errno));
+ return false;
+ }
+
+ strncpy(twhdr.start_of_header, TWRP, sizeof(twhdr.start_of_header));
+ strncpy(twhdr.type, TWSTREAMHDR, sizeof(twhdr.type));
+ twhdr.partition_count = partition_count;
+ twhdr.version = ADB_BACKUP_VERSION;
+ memset(twhdr.space, 0, sizeof(twhdr.space));
+ twhdr.crc = crc32(0L, Z_NULL, 0);
+ twhdr.crc = crc32(twhdr.crc, (const unsigned char*) &twhdr, sizeof(twhdr));
+ if (write(adb_control_bu_fd, &twhdr, sizeof(twhdr)) < 0) {
+ printf("Cannot write to adb control channel: %s\n", strerror(errno));
+ close(adb_control_bu_fd);
+ return false;
+ }
+ return true;
+}
+
+bool twadbbu::Write_ADB_Stream_Trailer() {
+ int adb_control_bu_fd;
+ struct AdbBackupControlType endadb;
+
+ memset(&endadb, 0, sizeof(endadb));
+
+ adb_control_bu_fd = open(TW_ADB_BU_CONTROL, O_WRONLY);
+ if (adb_control_bu_fd < 0) {
+ printf("Error opening adb_control_bu_fd: %s\n", strerror(errno));
+ return false;
+ }
+ strncpy(endadb.start_of_header, TWRP, sizeof(endadb.start_of_header));
+ strncpy(endadb.type, TWENDADB, sizeof(endadb.type));
+ endadb.crc = crc32(0L, Z_NULL, 0);
+ endadb.crc = crc32(endadb.crc, (const unsigned char*) &endadb, sizeof(endadb));
+ if (write(adb_control_bu_fd, &endadb, sizeof(endadb)) < 0) {
+ printf("Cannot write to ADB control: %s\n", strerror(errno));
+ close(adb_control_bu_fd);
+ return false;
+ }
+ close(adb_control_bu_fd);
+ return true;
+}
+
+bool twadbbu::Write_TWFN(std::string Backup_FileName, uint64_t file_size, bool use_compression) {
+ int adb_control_bu_fd;
+ adb_control_bu_fd = open(TW_ADB_BU_CONTROL, O_WRONLY | O_NONBLOCK);
+ struct twfilehdr twfilehdr;
+ strncpy(twfilehdr.start_of_header, TWRP, sizeof(twfilehdr.start_of_header));
+ strncpy(twfilehdr.type, TWFN, sizeof(twfilehdr.type));
+ strncpy(twfilehdr.name, Backup_FileName.c_str(), sizeof(twfilehdr.name));
+ twfilehdr.size = (file_size == 0 ? 1024 : file_size);
+ twfilehdr.compressed = use_compression;
+ twfilehdr.crc = crc32(0L, Z_NULL, 0);
+ twfilehdr.crc = crc32(twfilehdr.crc, (const unsigned char*) &twfilehdr, sizeof(twfilehdr));
+
+ printf("Sending TWFN to adb\n");
+ if (write(adb_control_bu_fd, &twfilehdr, sizeof(twfilehdr)) < 1) {
+ printf("Cannot that write to adb_control_bu_fd: %s\n", strerror(errno));
+ close(adb_control_bu_fd);
+ return false;
+ }
+ fsync(adb_control_bu_fd);
+ close(adb_control_bu_fd);
+ return true;
+}
+
+bool twadbbu::Write_TWIMG(std::string Backup_FileName, uint64_t file_size) {
+ int adb_control_bu_fd;
+ struct twfilehdr twimghdr;
+
+ adb_control_bu_fd = open(TW_ADB_BU_CONTROL, O_WRONLY | O_NONBLOCK);
+ strncpy(twimghdr.start_of_header, TWRP, sizeof(twimghdr.start_of_header));
+ strncpy(twimghdr.type, TWIMG, sizeof(twimghdr.type));
+ twimghdr.size = file_size;
+ strncpy(twimghdr.name, Backup_FileName.c_str(), sizeof(twimghdr.name));
+ twimghdr.crc = crc32(0L, Z_NULL, 0);
+ twimghdr.crc = crc32(twimghdr.crc, (const unsigned char*) &twimghdr, sizeof(twimghdr));
+ printf("Sending TWIMG to adb\n");
+ if (write(adb_control_bu_fd, &twimghdr, sizeof(twimghdr)) < 1) {
+ printf("Cannot write to adb control channel: %s\n", strerror(errno));
+ return false;
+ }
+
+ return true;
+}
+
+bool twadbbu::Write_TWEOF() {
+ struct AdbBackupControlType tweof;
+ int adb_control_bu_fd;
+ int errctr = 0;
+
+ printf("opening TW_ADB_BU_CONTROL\n");
+ adb_control_bu_fd = open(TW_ADB_BU_CONTROL, O_WRONLY | O_NONBLOCK);
+ while (adb_control_bu_fd < 0) {
+ printf("failed to open TW_ADB_BU_CONTROL. Retrying: %s\n", strerror(errno));
+ adb_control_bu_fd = open(TW_ADB_BU_CONTROL, O_WRONLY | O_NONBLOCK);
+ usleep(10000);
+ errctr++;
+ if (errctr > ADB_BU_MAX_ERROR) {
+ printf("Cannot write to adb_control_bu_fd: %s.\n", strerror(errno));
+ close(adb_control_bu_fd);
+ return false;
+ }
+ }
+ memset(&tweof, 0, sizeof(tweof));
+ strncpy(tweof.start_of_header, TWRP, sizeof(tweof.start_of_header));
+ strncpy(tweof.type, TWEOF, sizeof(tweof.type));
+ tweof.crc = crc32(0L, Z_NULL, 0);
+ tweof.crc = crc32(tweof.crc, (const unsigned char*) &tweof, sizeof(tweof));
+ printf("Sending TWEOF to adb backup\n");
+ if (write(adb_control_bu_fd, &tweof, sizeof(tweof)) < 0) {
+ printf("Cannot write to adb_control_bu_fd: %s.\n", strerror(errno));
+ close(adb_control_bu_fd);
+ return false;
+ }
+ close(adb_control_bu_fd);
+ return true;
+}
+
+bool twadbbu::Write_TWERROR() {
+ struct AdbBackupControlType twerror;
+ int adb_control_bu_fd = open(TW_ADB_BU_CONTROL, O_WRONLY | O_NONBLOCK);
+
+ strncpy(twerror.start_of_header, TWRP, sizeof(twerror.start_of_header));
+ strncpy(twerror.type, TWERROR, sizeof(twerror.type));
+ memset(twerror.space, 0, sizeof(twerror.space));
+ twerror.crc = crc32(0L, Z_NULL, 0);
+ twerror.crc = crc32(twerror.crc, (const unsigned char*) &twerror, sizeof(twerror));
+ if (write(adb_control_bu_fd, &twerror, sizeof(twerror)) < 0) {
+ printf("Cannot write to adb control channel: %s\n", strerror(errno));
+ return false;
+ }
+ close(adb_control_bu_fd);
+ return true;
+}
+
+bool twadbbu::Write_TWENDADB() {
+ struct AdbBackupControlType endadb;
+ int adb_control_bu_fd = open(TW_ADB_BU_CONTROL, O_WRONLY | O_NONBLOCK);
+
+ memset(&endadb, 0, sizeof(endadb));
+ strncpy(endadb.start_of_header, TWRP, sizeof(endadb.start_of_header));
+ strncpy(endadb.type, TWENDADB, sizeof(endadb.type));
+ endadb.crc = crc32(0L, Z_NULL, 0);
+ endadb.crc = crc32(endadb.crc, (const unsigned char*) &endadb, sizeof(endadb));
+
+ printf("Sending TWENDADB to ADB Backup\n");
+ if (write(adb_control_bu_fd, &endadb, sizeof(endadb)) < 1) {
+ printf("Cannot write to ADB_CONTROL_BU_FD: %s\n", strerror(errno));
+ return false;
+ }
+
+ close(adb_control_bu_fd);
+ return true;
+}
+
+bool twadbbu::Write_TWDATA(FILE* adbd_fp) {
+ struct AdbBackupControlType data_block;
+ memset(&data_block, 0, sizeof(data_block));
+ strncpy(data_block.start_of_header, TWRP, sizeof(data_block.start_of_header));
+ strncpy(data_block.type, TWDATA, sizeof(data_block.type));
+ data_block.crc = crc32(0L, Z_NULL, 0);
+ data_block.crc = crc32(data_block.crc, (const unsigned char*) &data_block, sizeof(data_block));
+ if (fwrite(&data_block, 1, sizeof(data_block), adbd_fp) != sizeof(data_block)) {
+ return false;
+ }
+ return true;
+}
diff --git a/adbbu/libtwadbbu.hpp b/adbbu/libtwadbbu.hpp
new file mode 100644
index 0000000..9244bb5
--- /dev/null
+++ b/adbbu/libtwadbbu.hpp
@@ -0,0 +1,51 @@
+/*
+ Copyright 2013 to 2017 TeamWin
+ TWRP is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ TWRP is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with TWRP. If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef _LIBTWADBBU_HPP
+#define _LIBTWADBBU_HPP
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/select.h>
+#include <sys/time.h>
+#include <string>
+#include <vector>
+#include <fstream>
+#include <sstream>
+
+#include "twadbstream.h"
+
+class twadbbu {
+public:
+ static bool Check_ADB_Backup_File(std::string fname); //Check if file is ADB Backup file
+ static std::vector<std::string> Get_ADB_Backup_Files(std::string fname); //List ADB Files in String Vector
+ static bool Write_ADB_Stream_Header(uint64_t partition_count); //Write ADB Stream Header to stream
+ static bool Write_ADB_Stream_Trailer(); //Write ADB Stream Trailer to stream
+ static bool Write_TWFN(std::string Backup_FileName, uint64_t file_size, bool use_compression); //Write a tar image to stream
+ static bool Write_TWIMG(std::string Backup_FileName, uint64_t file_size); //Write a partition image to stream
+ static bool Write_TWEOF(); //Write ADB End-Of-File marker to stream
+ static bool Write_TWERROR(); //Write error message occurred to stream
+ static bool Write_TWENDADB(); //Write ADB End-Of-Stream command to stream
+ static bool Write_TWDATA(FILE* adbd_fp); //Write TWDATA separator
+};
+
+#endif //__LIBTWADBBU_HPP
diff --git a/adbbu/twadbstream.h b/adbbu/twadbstream.h
new file mode 100644
index 0000000..bef463c
--- /dev/null
+++ b/adbbu/twadbstream.h
@@ -0,0 +1,112 @@
+/*
+ TWRP is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ TWRP is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with TWRP. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __TWADBSTREAM_H
+#define __TWADBSTREAM_H
+
+#define TWRPARG "--twrp"
+#define TWRP_BACKUP_ARG "backup"
+#define TWRP_RESTORE_ARG "restore"
+#define TWRP_STREAM_ARG "stream"
+#define TW_ADB_BACKUP "/tmp/twadbbackup" //FIFO for adb backup
+#define TW_ADB_RESTORE "/tmp/twadbrestore" //FIFO for adb restore
+#define TW_ADB_BU_CONTROL "/tmp/twadbbucontrol" //FIFO for sending control from TWRP to ADB Backup
+#define TW_ADB_TWRP_CONTROL "/tmp/twadbtwrpcontrol" //FIFO for sending control from ADB Backup to TWRP
+#define TWRP "TWRP" //Magic Value
+#define ADB_BU_MAX_ERROR 20 //Max amount of errors for while loops
+#define ADB_BACKUP_OP "adbbackup"
+#define ADB_RESTORE_OP "adbrestore"
+
+//ADB Backup Control Commands
+#define TWSTREAMHDR "twstreamheader" //TWRP Parititon Count Control
+#define TWFN "twfilename" //TWRP Filename Control
+#define TWIMG "twimage" //TWRP Image name Control
+#define TWEOF "tweof" //End of File for Image/File
+#define MD5TRAILER "md5trailer" //Image/File MD5 Trailer
+#define TWDATA "twdatablock" // twrp adb backup data block header
+#define TWMD5 "twverifymd5" //This command is compared to the md5trailer by ORS to verify transfer
+#define TWENDADB "twendadb" //End Protocol
+#define TWERROR "twerror" //Send error
+#define ADB_BACKUP_VERSION 3 //Backup Version
+#define DATA_MAX_CHUNK_SIZE 1048576 //Maximum size between each data header
+#define MAX_ADB_READ 512 //align with default tar size for amount to read fom adb stream
+
+/*
+structs for adb backup need to align to 512 bytes for reading 512
+bytes at a time
+Each struct contains a crc field so that when we are checking for commands
+and the crc doesn't match we still assume it's data matching the command
+struct but not really a command
+*/
+
+/* stream format:
+ | TW ADB Backup Header |
+ | TW File Stream Header |
+ | File Data |
+ | File/Image MD5 Trailer |
+ | TW File Stream Header |
+ | File Data |
+ | File/Image MD5 Trailer |
+ | etc... |
+*/
+
+//determine whether struct is 512 bytes, if not fail compilation
+#define ADBSTRUCT_STATIC_ASSERT(structure) typedef char adb_assertion[( !!(structure) )*2-1 ]
+
+//generic cmd structure to align fields for sending commands to and from adb backup
+struct AdbBackupControlType {
+ char start_of_header[8]; //stores the magic value #define TWRP
+ char type[16]; //stores the type of command, TWENDADB, TWCNT, TWEOF, TWMD5, TWDATA and TWERROR
+ uint32_t crc; //stores the zlib 32 bit crc of the AdbBackupControlType struct to allow for making sure we are processing metadata
+ char space[484]; //stores space to align the struct to 512 bytes
+
+ //return a C++ string while not reading outside the type char array
+ std::string get_type() {
+ return std::string(type, strnlen(type, sizeof(type)-1));
+ }
+};
+
+//general info for file metadata stored in adb backup header
+struct twfilehdr {
+ char start_of_header[8]; //stores the magic value #define TWRP
+ char type[16]; //stores the type of file header, TWFN or TWIMG
+ uint64_t size; //stores the size of the file contained after this header in the backup file
+ uint64_t compressed; //stores whether the file is compressed or not. 1 == compressed and 0 == uncompressed
+ uint32_t crc; //stores the zlib 32 bit crc of the twfilehdr struct to allow for making sure we are processing metadata
+ char name[468]; //stores the filename of the file
+};
+
+//md5 for files stored as a trailer to files in the adb backup file to check
+//that they are restored correctly
+struct AdbBackupFileTrailer {
+ char start_of_trailer[8]; //stores the magic value #define TWRP
+ char type[16]; //stores the AdbBackupFileTrailer type MD5TRAILER
+ uint32_t crc; //stores the zlib 32 bit crc of the AdbBackupFileTrailer struct to allow for making sure we are processing metadata
+ uint32_t ident; //stores crc to determine if header is encapsulated in stream as data
+ char md5[40]; //stores the md5 computation of the file
+ char space[440]; //stores space to align the struct to 512 bytes
+};
+
+//info for version and number of partitions backed up
+struct AdbBackupStreamHeader {
+ char start_of_header[8]; //stores the magic value #define TWRP
+ char type[16]; //stores the AdbBackupStreamHeader value TWCNT
+ uint64_t partition_count; //stores the number of partitions to restore in the stream
+ uint64_t version; //stores the version of adb backup. increment ADB_BACKUP_VERSION each time the metadata is updated
+ uint32_t crc; //stores the zlib 32 bit crc of the AdbBackupStreamHeader struct to allow for making sure we are processing metadata
+ char space[468]; //stores space to align the struct to 512 bytes
+};
+
+#endif //__TWADBSTREAM_H
diff --git a/adbbu/twrpback.cpp b/adbbu/twrpback.cpp
new file mode 100644
index 0000000..b3de76d
--- /dev/null
+++ b/adbbu/twrpback.cpp
@@ -0,0 +1,935 @@
+/*
+ Copyright 2013 to 2017 TeamWin
+ TWRP is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ TWRP is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with TWRP. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/select.h>
+#include <sys/time.h>
+#include <zlib.h>
+#include <ctype.h>
+#include <semaphore.h>
+#include <string>
+#include <sstream>
+#include <fstream>
+#include <algorithm>
+#include <utils/threads.h>
+#include <pthread.h>
+
+#include "twadbstream.h"
+#include "twrpback.hpp"
+#include "libtwadbbu.hpp"
+#include "../twrpDigest/twrpDigest.hpp"
+#include "../twrpDigest/twrpMD5.hpp"
+#include "../twrpAdbBuFifo.hpp"
+
+twrpback::twrpback(void) {
+ adbd_fp = NULL;
+ read_fd = 0;
+ write_fd = 0;
+ adb_control_twrp_fd = 0;
+ adb_control_bu_fd = 0;
+ adb_read_fd = 0;
+ adb_write_fd = 0;
+ ors_fd = 0;
+ debug_adb_fd = 0;
+ firstPart = true;
+ createFifos();
+ adbloginit();
+}
+
+twrpback::~twrpback(void) {
+ adblogfile.close();
+ closeFifos();
+}
+
+void twrpback::printErrMsg(std::string msg, int errNum) {
+ std::stringstream str;
+ str << strerror(errNum);
+ adblogwrite(msg + " " + str.str() + "\n");
+}
+
+void twrpback::createFifos(void) {
+ if (mkfifo(TW_ADB_BU_CONTROL, 0666) < 0) {
+ std::string msg = "Unable to create TW_ADB_BU_CONTROL fifo: ";
+ printErrMsg(msg, errno);
+ }
+ if (mkfifo(TW_ADB_TWRP_CONTROL, 0666) < 0) {
+ std::string msg = "Unable to create TW_ADB_TWRP_CONTROL fifo: ";
+ printErrMsg(msg, errno);
+ unlink(TW_ADB_BU_CONTROL);
+ }
+}
+
+void twrpback::closeFifos(void) {
+ if (unlink(TW_ADB_BU_CONTROL) < 0) {
+ std::string msg = "Unable to remove TW_ADB_BU_CONTROL: ";
+ printErrMsg(msg, errno);
+ }
+ if (unlink(TW_ADB_TWRP_CONTROL) < 0) {
+ std::string msg = "Unable to remove TW_ADB_TWRP_CONTROL: ";
+ printErrMsg(msg, errno);
+ }
+}
+
+void twrpback::adbloginit(void) {
+ adblogfile.open("/tmp/adb.log", std::fstream::app);
+}
+
+void twrpback::adblogwrite(std::string writemsg) {
+ adblogfile << writemsg << std::flush;
+}
+
+void twrpback::close_backup_fds() {
+ if (ors_fd > 0)
+ close(ors_fd);
+ if (write_fd > 0)
+ close(write_fd);
+ if (adb_read_fd > 0)
+ close(adb_read_fd);
+ if (adb_control_bu_fd > 0)
+ close(adb_control_bu_fd);
+ #ifdef _DEBUG_ADB_BACKUP
+ if (debug_adb_fd > 0)
+ close(debug_adb_fd);
+ #endif
+ if (adbd_fp != NULL)
+ fclose(adbd_fp);
+ if (access(TW_ADB_BACKUP, F_OK) == 0)
+ unlink(TW_ADB_BACKUP);
+}
+
+void twrpback::close_restore_fds() {
+ if (ors_fd > 0)
+ close(ors_fd);
+ if (write_fd > 0)
+ close(write_fd);
+ if (adb_control_bu_fd > 0)
+ close(adb_control_bu_fd);
+ if (adb_control_twrp_fd > 0)
+ close(adb_control_twrp_fd);
+ if (adbd_fp != NULL)
+ fclose(adbd_fp);
+ if (access(TW_ADB_RESTORE, F_OK) == 0)
+ unlink(TW_ADB_RESTORE);
+ #ifdef _DEBUG_ADB_BACKUP
+ if (debug_adb_fd > 0)
+ close(debug_adb_fd);
+ #endif
+}
+
+bool twrpback::backup(std::string command) {
+ twrpMD5 digest;
+ int bytes = 0, errctr = 0;
+ char adbReadStream[MAX_ADB_READ];
+ uint64_t totalbytes = 0, dataChunkBytes = 0, fileBytes = 0;
+ uint64_t md5fnsize = 0;
+ struct AdbBackupControlType endadb;
+
+ //ADBSTRUCT_STATIC_ASSERT(sizeof(endadb) == MAX_ADB_READ);
+
+ bool writedata = true;
+ bool compressed = false;
+ bool firstDataPacket = true;
+
+ adbd_fp = fdopen(adbd_fd, "w");
+ if (adbd_fp == NULL) {
+ adblogwrite("Unable to open adb_fp\n");
+ return false;
+ }
+
+ if (mkfifo(TW_ADB_BACKUP, 0666) < 0) {
+ adblogwrite("Unable to create TW_ADB_BACKUP fifo\n");
+ return false;
+ }
+
+ adblogwrite("opening TW_ADB_FIFO\n");
+
+ write_fd = open(TW_ADB_FIFO, O_WRONLY);
+ while (write_fd < 0) {
+ write_fd = open(TW_ADB_FIFO, O_WRONLY);
+ usleep(10000);
+ errctr++;
+ if (errctr > ADB_BU_MAX_ERROR) {
+ std::string msg = "Unable to open TW_ADB_FIFO";
+ printErrMsg(msg, errno);
+ close_backup_fds();
+ return false;
+ }
+ }
+
+ memset(operation, 0, sizeof(operation));
+ if (snprintf(operation, sizeof(operation), "adbbackup %s", command.c_str()) >= (int)sizeof(operation)) {
+ adblogwrite("Operation too big to write to ORS_INPUT_FILE\n");
+ close_backup_fds();
+ return false;
+ }
+ if (write(write_fd, operation, sizeof(operation)) != sizeof(operation)) {
+ adblogwrite("Unable to write to ORS_INPUT_FILE\n");
+ close_backup_fds();
+ return false;
+ }
+
+ memset(&adbReadStream, 0, sizeof(adbReadStream));
+ memset(&cmd, 0, sizeof(cmd));
+
+ adblogwrite("opening TW_ADB_BU_CONTROL\n");
+ adb_control_bu_fd = open(TW_ADB_BU_CONTROL, O_RDONLY | O_NONBLOCK);
+ if (adb_control_bu_fd < 0) {
+ adblogwrite("Unable to open TW_ADB_BU_CONTROL for reading.\n");
+ close_backup_fds();
+ return false;
+ }
+
+ adblogwrite("opening TW_ADB_BACKUP\n");
+ adb_read_fd = open(TW_ADB_BACKUP, O_RDONLY | O_NONBLOCK);
+ if (adb_read_fd < 0) {
+ adblogwrite("Unable to open TW_ADB_BACKUP for reading.\n");
+ close_backup_fds();
+ return false;
+ }
+
+ //loop until TWENDADB sent
+ while (true) {
+ if (read(adb_control_bu_fd, &cmd, sizeof(cmd)) > 0) {
+ struct AdbBackupControlType structcmd;
+
+ memcpy(&structcmd, cmd, sizeof(cmd));
+ std::string cmdtype = structcmd.get_type();
+
+ //we received an error, exit and unlink
+ if (cmdtype == TWERROR) {
+ writedata = false;
+ adblogwrite("Error received. Quitting...\n");
+ close_backup_fds();
+ return false;
+ }
+ //we received the end of adb backup stream so we should break the loop
+ else if (cmdtype == TWENDADB) {
+ writedata = false;
+ adblogwrite("Recieved TWENDADB\n");
+ memcpy(&endadb, cmd, sizeof(cmd));
+ std::stringstream str;
+ str << totalbytes;
+ adblogwrite(str.str() + " total bytes written\n");
+ break;
+ }
+ //we recieved the TWSTREAMHDR structure metadata to write to adb
+ else if (cmdtype == TWSTREAMHDR) {
+ writedata = false;
+ adblogwrite("writing TWSTREAMHDR\n");
+ if (fwrite(cmd, 1, sizeof(cmd), adbd_fp) != sizeof(cmd)) {
+ std::string msg = "Error writing TWSTREAMHDR to adbd";
+ printErrMsg(msg, errno);
+ close_backup_fds();
+ return false;
+ }
+ fflush(adbd_fp);
+ }
+ //we will be writing an image from TWRP
+ else if (cmdtype == TWIMG) {
+ struct twfilehdr twimghdr;
+
+ adblogwrite("writing TWIMG\n");
+ digest.init();
+ memset(&twimghdr, 0, sizeof(twimghdr));
+ memcpy(&twimghdr, cmd, sizeof(cmd));
+ md5fnsize = twimghdr.size;
+ compressed = false;
+
+ #ifdef _DEBUG_ADB_BACKUP
+ std::string debug_fname = "/data/media/";
+ debug_fname.append(basename(twimghdr.name));
+ debug_fname.append("-backup.img");
+ debug_adb_fd = open(debug_fname.c_str(), O_WRONLY | O_CREAT, 0666);
+ adblogwrite("Opening adb debug tar\n");
+ #endif
+
+ if (fwrite(cmd, 1, sizeof(cmd), adbd_fp) != sizeof(cmd)) {
+ adblogwrite("Error writing TWIMG to adbd\n");
+ close_backup_fds();
+ return false;
+ }
+ fflush(adbd_fp);
+ writedata = true;
+ }
+ //we will be writing a tar from TWRP
+ else if (cmdtype == TWFN) {
+ struct twfilehdr twfilehdr;
+
+ adblogwrite("writing TWFN\n");
+ digest.init();
+
+ //ADBSTRUCT_STATIC_ASSERT(sizeof(twfilehdr) == MAX_ADB_READ);
+
+ memset(&twfilehdr, 0, sizeof(twfilehdr));
+ memcpy(&twfilehdr, cmd, sizeof(cmd));
+ md5fnsize = twfilehdr.size;
+
+ compressed = twfilehdr.compressed == 1 ? true: false;
+
+ #ifdef _DEBUG_ADB_BACKUP
+ std::string debug_fname = "/data/media/";
+ debug_fname.append(basename(twfilehdr.name));
+ debug_fname.append("-backup.tar");
+ debug_adb_fd = open(debug_fname.c_str(), O_WRONLY | O_CREAT, 0666);
+ adblogwrite("Opening adb debug tar\n");
+ #endif
+
+ if (fwrite(cmd, 1, sizeof(cmd), adbd_fp) != sizeof(cmd)) {
+ adblogwrite("Error writing TWFN to adbd\n");
+ close_backup_fds();
+ return false;
+ }
+ fflush(adbd_fp);
+ writedata = true;
+ }
+ /*
+ We received the command that we are done with the file stream.
+ We will flush the remaining data stream.
+ Update md5 and write final results to adb stream.
+ If we need padding because the total bytes are not a multiple
+ of 512, we pad the end with 0s to we reach 512.
+ We also write the final md5 to the adb stream.
+ */
+ else if (cmdtype == TWEOF) {
+ adblogwrite("received TWEOF\n");
+ while ((bytes = read(adb_read_fd, &adbReadStream, sizeof(adbReadStream)) != 0)) {
+ totalbytes += bytes;
+ fileBytes += bytes;
+ dataChunkBytes += bytes;
+
+ char *writeAdbReadStream = new char [bytes];
+ memcpy(writeAdbReadStream, adbReadStream, bytes);
+
+ digest.update((unsigned char *) writeAdbReadStream, bytes);
+ if (fwrite(writeAdbReadStream, 1, bytes, adbd_fp) < 0) {
+ std::string msg = "Cannot write to adbd stream: ";
+ printErrMsg(msg, errno);
+ }
+ #if defined(_DEBUG_ADB_BACKUP)
+ if (write(debug_adb_fd, writeAdbReadStream, bytes) < 1) {
+ std::string msg = "Cannot write to ADB_CONTROL_READ_FD: ";
+ printErrMsg(msg, errno);
+ close_restore_fds();
+ return false;
+ }
+ #endif
+ fflush(adbd_fp);
+ delete [] writeAdbReadStream;
+ memset(adbReadStream, 0, sizeof(adbReadStream));
+ }
+
+ if (fileBytes % DATA_MAX_CHUNK_SIZE != 0) {
+ int64_t count = fileBytes / DATA_MAX_CHUNK_SIZE + 1;
+ uint64_t ceilingBytes = count * DATA_MAX_CHUNK_SIZE;
+ char padding[ceilingBytes - fileBytes];
+ int paddingBytes = sizeof(padding);
+ memset(padding, 0, paddingBytes);
+ std::stringstream paddingStr;
+ paddingStr << paddingBytes;
+ adblogwrite("writing padding to stream: " + paddingStr.str() + " bytes\n");
+ if (fwrite(padding, 1, paddingBytes, adbd_fp) != sizeof(padding)) {
+ adblogwrite("Error writing padding to adbd\n");
+ close_backup_fds();
+ return false;
+ }
+ #if defined(_DEBUG_ADB_BACKUP)
+ if (write(debug_adb_fd, padding, paddingBytes) < 1) {
+ std::string msg = "Cannot write to ADB_CONTROL_READ_FD: ";
+ printErrMsg(msg, errno);
+ close_restore_fds();
+ return false;
+ }
+ #endif
+ totalbytes += paddingBytes;
+ digest.update((unsigned char *) padding, paddingBytes);
+ fflush(adbd_fp);
+ }
+
+ AdbBackupFileTrailer md5trailer;
+
+ memset(&md5trailer, 0, sizeof(md5trailer));
+
+ std::string md5string = digest.return_digest_string();
+
+ strncpy(md5trailer.start_of_trailer, TWRP, sizeof(md5trailer.start_of_trailer));
+ strncpy(md5trailer.type, MD5TRAILER, sizeof(md5trailer.type));
+ strncpy(md5trailer.md5, md5string.c_str(), sizeof(md5trailer.md5));
+
+ md5trailer.crc = crc32(0L, Z_NULL, 0);
+ md5trailer.crc = crc32(md5trailer.crc, (const unsigned char*) &md5trailer, sizeof(md5trailer));
+
+ md5trailer.ident = crc32(0L, Z_NULL, 0);
+ md5trailer.ident = crc32(md5trailer.ident, (const unsigned char*) &md5trailer, sizeof(md5trailer));
+ md5trailer.ident = crc32(md5trailer.ident, (const unsigned char*) &md5fnsize, sizeof(md5fnsize));
+
+ if (fwrite(&md5trailer, 1, sizeof(md5trailer), adbd_fp) != sizeof(md5trailer)) {
+ adblogwrite("Error writing md5trailer to adbd\n");
+ close_backup_fds();
+ return false;
+ }
+ fflush(adbd_fp);
+ writedata = false;
+ firstDataPacket = true;
+ fileBytes = 0;
+ }
+ memset(&cmd, 0, sizeof(cmd));
+ dataChunkBytes = 0;
+ }
+ //If we are to write data because of a new file stream, lets write all the data.
+ //This will allow us to not write data after a command structure has been written
+ //to the adb stream.
+ //If the stream is compressed, we need to always write the data.
+ if (writedata || compressed) {
+ while ((bytes = read(adb_read_fd, &adbReadStream, sizeof(adbReadStream))) > 0) {
+ if (firstDataPacket) {
+ if (!twadbbu::Write_TWDATA(adbd_fp)) {
+ close_backup_fds();
+ return false;
+ }
+ fileBytes += MAX_ADB_READ;
+ fflush(adbd_fp);
+ firstDataPacket = false;
+ dataChunkBytes += sizeof(adbReadStream);
+ }
+ char *writeAdbReadStream = new char [bytes];
+ memcpy(writeAdbReadStream, adbReadStream, bytes);
+
+ digest.update((unsigned char *) writeAdbReadStream, bytes);
+
+ totalbytes += bytes;
+ fileBytes += bytes;
+ dataChunkBytes += bytes;
+
+ if (fwrite(writeAdbReadStream, 1, bytes, adbd_fp) != (unsigned long long)bytes) {
+ adblogwrite("Error writing backup data to adbd\n");
+ close_backup_fds();
+ return false;
+ }
+ #ifdef _DEBUG_ADB_BACKUP
+ if (write(debug_adb_fd, writeAdbReadStream, bytes) < 1) {
+ std::string msg = "Cannot write to ADB_CONTROL_READ_FD: ";
+ printErrMsg(msg, errno);
+ close_restore_fds();
+ return false;
+ }
+ #endif
+ fflush(adbd_fp);
+ delete [] writeAdbReadStream;
+
+ memset(&adbReadStream, 0, sizeof(adbReadStream));
+
+ if (dataChunkBytes == DATA_MAX_CHUNK_SIZE) {
+ dataChunkBytes = 0;
+ firstDataPacket = true;
+ }
+ else if (dataChunkBytes > (DATA_MAX_CHUNK_SIZE - sizeof(adbReadStream))) {
+ int bytesLeft = DATA_MAX_CHUNK_SIZE - dataChunkBytes;
+ char extraData[bytesLeft];
+
+ memset(&extraData, 0, bytesLeft);
+ while ((bytes = read(adb_read_fd, &extraData, bytesLeft)) != 0) {
+ if (bytes > 0) {
+ totalbytes += bytes;
+ fileBytes += bytes;
+ dataChunkBytes += bytes;
+
+ bytesLeft -= bytes;
+ char *writeAdbReadStream = new char [bytes];
+ memcpy(writeAdbReadStream, extraData, bytes);
+
+ digest.update((unsigned char *) writeAdbReadStream, bytes);
+ if (fwrite(writeAdbReadStream, 1, bytes, adbd_fp) < 0) {
+ std::string msg = "Cannot write to adbd stream: ";
+ printErrMsg(msg, errno);
+ close_restore_fds();
+ return false;
+ }
+ #ifdef _DEBUG_ADB_BACKUP
+ if (write(debug_adb_fd, writeAdbReadStream, bytes) < 1) {
+ std::string msg = "Cannot write to ADB_CONTROL_READ_FD: ";
+ printErrMsg(msg, errno);
+ close_restore_fds();
+ return false;
+ }
+ #endif
+ fflush(adbd_fp);
+ delete [] writeAdbReadStream;
+ }
+ memset(&extraData, 0, bytesLeft);
+ if (bytesLeft == 0) {
+ break;
+ }
+ }
+
+ fflush(adbd_fp);
+ dataChunkBytes = 0;
+ firstDataPacket = true;
+ }
+ }
+ }
+ }
+
+ //Write the final end adb structure to the adb stream
+ if (fwrite(&endadb, 1, sizeof(endadb), adbd_fp) != sizeof(endadb)) {
+ adblogwrite("Error writing endadb to adbd\n");
+ close_backup_fds();
+ return false;
+ }
+ fflush(adbd_fp);
+ close_backup_fds();
+ return true;
+}
+
+bool twrpback::restore(void) {
+ twrpMD5 digest;
+ char cmd[MAX_ADB_READ];
+ char readAdbStream[MAX_ADB_READ];
+ struct AdbBackupControlType structcmd;
+ int errctr = 0;
+ uint64_t totalbytes = 0, dataChunkBytes = 0;
+ uint64_t md5fnsize = 0, fileBytes = 0;
+ bool read_from_adb;
+ bool md5sumdata;
+ bool compressed, tweofrcvd, extraData;
+
+ read_from_adb = true;
+
+ signal(SIGPIPE, SIG_IGN);
+ signal(SIGHUP, SIG_IGN);
+
+ adbd_fp = fdopen(adbd_fd, "r");
+ if (adbd_fp == NULL) {
+ adblogwrite("Unable to open adb_fp\n");
+ close_restore_fds();
+ return false;
+ }
+
+ if(mkfifo(TW_ADB_RESTORE, 0666)) {
+ adblogwrite("Unable to create TW_ADB_RESTORE fifo\n");
+ close_restore_fds();
+ return false;
+ }
+
+ adblogwrite("opening TW_ADB_FIFO\n");
+ write_fd = open(TW_ADB_FIFO, O_WRONLY);
+
+ while (write_fd < 0) {
+ write_fd = open(TW_ADB_FIFO, O_WRONLY);
+ errctr++;
+ if (errctr > ADB_BU_MAX_ERROR) {
+ std::string msg = "Unable to open TW_ADB_FIFO.";
+ printErrMsg(msg, errno);
+ close_restore_fds();
+ return false;
+ }
+ }
+
+ memset(operation, 0, sizeof(operation));
+ sprintf(operation, "adbrestore");
+ if (write(write_fd, operation, sizeof(operation)) != sizeof(operation)) {
+ adblogwrite("Unable to write to TW_ADB_FIFO\n");
+ close_restore_fds();
+ return false;
+ }
+
+ memset(&readAdbStream, 0, sizeof(readAdbStream));
+ memset(&cmd, 0, sizeof(cmd));
+
+ adblogwrite("opening TW_ADB_BU_CONTROL\n");
+ adb_control_bu_fd = open(TW_ADB_BU_CONTROL, O_RDONLY | O_NONBLOCK);
+ if (adb_control_bu_fd < 0) {
+ std::string msg = "Unable to open TW_ADB_BU_CONTROL for writing.";
+ printErrMsg(msg, errno);
+ close_restore_fds();
+ return false;
+ }
+
+ adblogwrite("opening TW_ADB_TWRP_CONTROL\n");
+ adb_control_twrp_fd = open(TW_ADB_TWRP_CONTROL, O_WRONLY | O_NONBLOCK);
+ if (adb_control_twrp_fd < 0) {
+ std::string msg = "Unable to open TW_ADB_TWRP_CONTROL for writing. Retrying...";
+ printErrMsg(msg, errno);
+ while (adb_control_twrp_fd < 0) {
+ adb_control_twrp_fd = open(TW_ADB_TWRP_CONTROL, O_WRONLY | O_NONBLOCK);
+ usleep(10000);
+ errctr++;
+ if (errctr > ADB_BU_MAX_ERROR) {
+ adblogwrite("Unable to open TW_ADB_TWRP_CONTROL\n");
+ close_backup_fds();
+ return false;
+ }
+ }
+ }
+
+ //Loop until we receive TWENDADB from TWRP
+ while (true) {
+ memset(&cmd, 0, sizeof(cmd));
+ if (read(adb_control_bu_fd, &cmd, sizeof(cmd)) > 0) {
+ struct AdbBackupControlType structcmd;
+ memcpy(&structcmd, cmd, sizeof(cmd));
+ std::string cmdtype = structcmd.get_type();
+
+ //If we receive TWEOF from TWRP close adb data fifo
+ if (cmdtype == TWEOF) {
+ adblogwrite("Received TWEOF\n");
+ read_from_adb = true;
+ tweofrcvd = true;
+ close(adb_write_fd);
+ }
+ //Break when TWRP sends TWENDADB
+ else if (cmdtype == TWENDADB) {
+ adblogwrite("Received TWENDADB\n");
+ break;
+ }
+ //we received an error, exit and unlink
+ else if (cmdtype == TWERROR) {
+ adblogwrite("Error received. Quitting...\n");
+ close_restore_fds();
+ return false;
+ }
+ }
+ //If we should read from the adb stream, write commands and data to TWRP
+ if (read_from_adb) {
+ int readbytes;
+ if ((readbytes = fread(readAdbStream, 1, sizeof(readAdbStream), adbd_fp)) == sizeof(readAdbStream)) {
+ memcpy(&structcmd, readAdbStream, sizeof(readAdbStream));
+ std::string cmdtype = structcmd.get_type();
+
+ //Tell TWRP we have read the entire adb stream
+ if (cmdtype == TWENDADB) {
+ struct AdbBackupControlType endadb;
+ uint32_t crc, endadbcrc;
+
+ md5sumdata = false;
+ memset(&endadb, 0, sizeof(endadb));
+ memcpy(&endadb, readAdbStream, sizeof(readAdbStream));
+ endadbcrc = endadb.crc;
+ memset(&endadb.crc, 0, sizeof(endadb.crc));
+ crc = crc32(0L, Z_NULL, 0);
+ crc = crc32(crc, (const unsigned char*) &endadb, sizeof(endadb));
+
+ if (crc == endadbcrc) {
+ adblogwrite("sending TWENDADB\n");
+ if (write(adb_control_twrp_fd, &endadb, sizeof(endadb)) < 1) {
+ std::string msg = "Cannot write to ADB_CONTROL_READ_FD: ";
+ printErrMsg(msg, errno);
+ close_restore_fds();
+ return false;
+ }
+ read_from_adb = false;
+ }
+ else {
+ adblogwrite("ADB TWENDADB crc header doesn't match\n");
+ close_restore_fds();
+ return false;
+ }
+ }
+ //Send TWRP partition metadata
+ else if (cmdtype == TWSTREAMHDR) {
+ struct AdbBackupStreamHeader cnthdr;
+ uint32_t crc, cnthdrcrc;
+
+ //ADBSTRUCT_STATIC_ASSERT(sizeof(cnthdr) == MAX_ADB_READ);
+
+ md5sumdata = false;
+ memset(&cnthdr, 0, sizeof(cnthdr));
+ memcpy(&cnthdr, readAdbStream, sizeof(readAdbStream));
+ cnthdrcrc = cnthdr.crc;
+ memset(&cnthdr.crc, 0, sizeof(cnthdr.crc));
+ crc = crc32(0L, Z_NULL, 0);
+ crc = crc32(crc, (const unsigned char*) &cnthdr, sizeof(cnthdr));
+
+ if (crc == cnthdrcrc) {
+ adblogwrite("Restoring TWSTREAMHDR\n");
+ if (write(adb_control_twrp_fd, readAdbStream, sizeof(readAdbStream)) < 0) {
+ std::string msg = "Cannot write to adb_control_twrp_fd: ";
+ printErrMsg(msg, errno);
+ close_restore_fds();
+ return false;
+ }
+ }
+ else {
+ adblogwrite("ADB TWSTREAMHDR crc header doesn't match\n");
+ close_restore_fds();
+ return false;
+ }
+ }
+ //Tell TWRP we are sending a partition image
+ else if (cmdtype == TWIMG) {
+ struct twfilehdr twimghdr;
+ uint32_t crc, twimghdrcrc;
+ md5sumdata = false;
+ fileBytes = 0;
+ read_from_adb = true;
+ dataChunkBytes = 0;
+ extraData = false;
+
+ digest.init();
+ adblogwrite("Restoring TWIMG\n");
+ memset(&twimghdr, 0, sizeof(twimghdr));
+ memcpy(&twimghdr, readAdbStream, sizeof(readAdbStream));
+ md5fnsize = twimghdr.size;
+ twimghdrcrc = twimghdr.crc;
+ memset(&twimghdr.crc, 0, sizeof(twimghdr.crc));
+
+ crc = crc32(0L, Z_NULL, 0);
+ crc = crc32(crc, (const unsigned char*) &twimghdr, sizeof(twimghdr));
+ if (crc == twimghdrcrc) {
+ if (write(adb_control_twrp_fd, readAdbStream, sizeof(readAdbStream)) < 1) {
+ std::string msg = "Cannot write to adb_control_twrp_fd: ";
+ printErrMsg(msg, errno);
+ close_restore_fds();
+ return false;
+ }
+ }
+ else {
+ adblogwrite("ADB TWIMG crc header doesn't match\n");
+ close_restore_fds();
+ return false;
+ }
+
+ #ifdef _DEBUG_ADB_BACKUP
+ std::string debug_fname = "/data/media/";
+ debug_fname.append(basename(twimghdr.name));
+ debug_fname.append("-restore.img");
+ adblogwrite("image: " + debug_fname + "\n");
+ debug_adb_fd = open(debug_fname.c_str(), O_WRONLY | O_CREAT, 0666);
+ adblogwrite("Opened restore image\n");
+ #endif
+
+ adblogwrite("opening TW_ADB_RESTORE\n");
+ adb_write_fd = open(TW_ADB_RESTORE, O_WRONLY);
+ }
+ //Tell TWRP we are sending a tar stream
+ else if (cmdtype == TWFN) {
+ struct twfilehdr twfilehdr;
+ uint32_t crc, twfilehdrcrc;
+ fileBytes = 0;
+ md5sumdata = false;
+ read_from_adb = true;
+ dataChunkBytes = 0;
+ extraData = false;
+
+ digest.init();
+ adblogwrite("Restoring TWFN\n");
+ memset(&twfilehdr, 0, sizeof(twfilehdr));
+ memcpy(&twfilehdr, readAdbStream, sizeof(readAdbStream));
+ md5fnsize = twfilehdr.size;
+ twfilehdrcrc = twfilehdr.crc;
+ memset(&twfilehdr.crc, 0, sizeof(twfilehdr.crc));
+
+ crc = crc32(0L, Z_NULL, 0);
+ crc = crc32(crc, (const unsigned char*) &twfilehdr, sizeof(twfilehdr));
+
+ if (crc == twfilehdrcrc) {
+ if (write(adb_control_twrp_fd, readAdbStream, sizeof(readAdbStream)) < 1) {
+ std::string msg = "Cannot write to adb_control_twrp_fd: ";
+ printErrMsg(msg, errno);
+ close_restore_fds();
+ return false;
+ }
+ }
+ else {
+ adblogwrite("ADB TWFN crc header doesn't match\n");
+ close_restore_fds();
+ return false;
+ }
+
+ #ifdef _DEBUG_ADB_BACKUP
+ std::string debug_fname = "/data/media/";
+ debug_fname.append(basename(twfilehdr.name));
+ debug_fname.append("-restore.tar");
+ adblogwrite("tar: " + debug_fname + "\n");
+ debug_adb_fd = open(debug_fname.c_str(), O_WRONLY | O_CREAT, 0666);
+ adblogwrite("Opened restore tar\n");
+ #endif
+
+ compressed = twfilehdr.compressed == 1 ? true: false;
+ adblogwrite("opening TW_ADB_RESTORE\n");
+ adb_write_fd = open(TW_ADB_RESTORE, O_WRONLY);
+ }
+ else if (cmdtype == MD5TRAILER) {
+ if (fileBytes >= md5fnsize)
+ close(adb_write_fd);
+ if (tweofrcvd) {
+ read_from_adb = true;
+ tweofrcvd = false;
+ }
+ else
+ read_from_adb = false; //don't read from adb until TWRP sends TWEOF
+ md5sumdata = false;
+ if (!checkMD5Trailer(readAdbStream, md5fnsize, &digest)) {
+ close_restore_fds();
+ break;
+ }
+ continue;
+ }
+ //Send the tar or partition image md5 to TWRP
+ else if (cmdtype == TWDATA) {
+ dataChunkBytes += sizeof(readAdbStream);
+ while (true) {
+ if ((readbytes = fread(readAdbStream, 1, sizeof(readAdbStream), adbd_fp)) != sizeof(readAdbStream)) {
+ close_restore_fds();
+ return false;
+ }
+ memcpy(&structcmd, readAdbStream, sizeof(readAdbStream));
+ std::string cmdtype = structcmd.get_type();
+
+ dataChunkBytes += readbytes;
+ totalbytes += readbytes;
+ fileBytes += readbytes;
+
+ if (cmdtype == MD5TRAILER) {
+ if (fileBytes >= md5fnsize)
+ close(adb_write_fd);
+ if (tweofrcvd) {
+ tweofrcvd = false;
+ read_from_adb = true;
+ }
+ else
+ read_from_adb = false; //don't read from adb until TWRP sends TWEOF
+ if (!checkMD5Trailer(readAdbStream, md5fnsize, &digest)) {
+ close_restore_fds();
+ break;
+ }
+ break;
+ }
+
+ digest.update((unsigned char*)readAdbStream, readbytes);
+
+ read_from_adb = true;
+
+ #ifdef _DEBUG_ADB_BACKUP
+ if (write(debug_adb_fd, readAdbStream, sizeof(readAdbStream)) < 0) {
+ std::string msg = "Cannot write to ADB_CONTROL_READ_FD: ";
+ printErrMsg(msg, errno);
+ close_restore_fds();
+ return false;
+ }
+ #endif
+
+ if (write(adb_write_fd, readAdbStream, sizeof(readAdbStream)) < 0) {
+ std::string msg = "Cannot write to TWRP ADB FIFO: ";
+ md5sumdata = true;
+ printErrMsg(msg, errno);
+ adblogwrite("end of stream reached.\n");
+ break;
+ }
+
+ if (dataChunkBytes == DATA_MAX_CHUNK_SIZE) {
+ dataChunkBytes = 0;
+ md5sumdata = false;
+ break;
+ }
+ }
+ }
+ else if (md5sumdata) {
+ digest.update((unsigned char*)readAdbStream, sizeof(readAdbStream));
+ md5sumdata = true;
+ }
+ }
+ }
+ }
+ std::stringstream str;
+ str << totalbytes;
+ close_restore_fds();
+ adblogwrite(str.str() + " bytes restored from adbbackup\n");
+ return true;
+}
+
+void twrpback::streamFileForTWRP(void) {
+ adblogwrite("streamFileForTwrp" + streamFn + "\n");
+}
+
+void twrpback::setStreamFileName(std::string fn) {
+ streamFn = fn;
+ adbd_fd = open(fn.c_str(), O_RDONLY);
+ if (adbd_fd < 0) {
+ adblogwrite("Unable to open adb_fd\n");
+ close(adbd_fd);
+ return;
+ }
+ restore();
+}
+
+void twrpback::threadStream(void) {
+ pthread_t thread;
+ ThreadPtr streamPtr = &twrpback::streamFileForTWRP;
+ PThreadPtr p = *(PThreadPtr*)&streamPtr;
+ pthread_create(&thread, NULL, p, this);
+ pthread_join(thread, NULL);
+}
+
+bool twrpback::checkMD5Trailer(char readAdbStream[], uint64_t md5fnsize, twrpMD5 *digest) {
+ struct AdbBackupFileTrailer md5tr;
+ uint32_t crc, md5trcrc, md5ident, md5identmatch;
+
+ //ADBSTRUCT_STATIC_ASSERT(sizeof(md5tr) == MAX_ADB_READ);
+ memcpy(&md5tr, readAdbStream, MAX_ADB_READ);
+ md5ident = md5tr.ident;
+
+ memset(&md5tr.ident, 0, sizeof(md5tr.ident));
+
+ md5identmatch = crc32(0L, Z_NULL, 0);
+ md5identmatch = crc32(md5identmatch, (const unsigned char*) &md5tr, sizeof(md5tr));
+ md5identmatch = crc32(md5identmatch, (const unsigned char*) &md5fnsize, sizeof(md5fnsize));
+
+ if (md5identmatch == md5ident) {
+ adblogwrite("checking MD5TRAILER\n");
+ md5trcrc = md5tr.crc;
+ memset(&md5tr.crc, 0, sizeof(md5tr.crc));
+ crc = crc32(0L, Z_NULL, 0);
+ crc = crc32(crc, (const unsigned char*) &md5tr, sizeof(md5tr));
+ if (crc == md5trcrc) {
+ if (write(adb_control_twrp_fd, &md5tr, sizeof(md5tr)) < 1) {
+ std::string msg = "Cannot write to adb_control_twrp_fd: ";
+ printErrMsg(msg, errno);
+ close_restore_fds();
+ return false;
+ }
+ }
+ else {
+ adblogwrite("ADB MD5TRAILER crc header doesn't match\n");
+ close_restore_fds();
+ return false;
+ }
+
+ AdbBackupFileTrailer md5;
+
+ memset(&md5, 0, sizeof(md5));
+ strncpy(md5.start_of_trailer, TWRP, sizeof(md5.start_of_trailer));
+ strncpy(md5.type, TWMD5, sizeof(md5.type));
+ std::string md5string = digest->return_digest_string();
+ strncpy(md5.md5, md5string.c_str(), sizeof(md5.md5));
+
+ adblogwrite("sending MD5 verification: " + md5string + "\n");
+ if (write(adb_control_twrp_fd, &md5, sizeof(md5)) < 1) {
+ std::string msg = "Cannot write to adb_control_twrp_fd: ";
+ printErrMsg(msg, errno);
+ close_restore_fds();
+ return false;
+ }
+ return true;
+ }
+ return false;
+}
diff --git a/adbbu/twrpback.hpp b/adbbu/twrpback.hpp
new file mode 100644
index 0000000..edc1626
--- /dev/null
+++ b/adbbu/twrpback.hpp
@@ -0,0 +1,61 @@
+/*
+ Copyright 2013 to 2017 TeamWin
+ TWRP is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ TWRP is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with TWRP. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _TWRPBACK_HPP
+#define _TWRPBACK_HPP
+
+#include <fstream>
+#include "../twrpDigest/twrpMD5.hpp"
+
+class twrpback {
+public:
+ int adbd_fd; // adbd data stream
+ twrpback(void);
+ virtual ~twrpback(void);
+ bool backup(std::string command); // adb backup stream
+ bool restore(void); // adb restore stream
+ void adblogwrite(std::string writemsg); // adb debugging log function
+ void createFifos(void); // create fifos needed for adb backup
+ void closeFifos(void); // close created fifos
+ void streamFileForTWRP(void); // stream file to twrp via bu
+ void setStreamFileName(std::string fn); // tell adb backup what file to load on storage
+ void threadStream(void); // thread bu for streaming
+
+private:
+ int read_fd; // ors input fd
+ int write_fd; // ors operation fd
+ int ors_fd; // ors output fd
+ int adb_control_twrp_fd; // fd for bu to twrp communication
+ int adb_control_bu_fd; // fd for twrp to bu communication
+ int adb_read_fd; // adb read data stream
+ int adb_write_fd; // adb write data stream
+ int debug_adb_fd; // fd to write debug tars
+ bool firstPart; // first partition in the stream
+ FILE *adbd_fp; // file pointer for adb stream
+ char cmd[512]; // store result of commands
+ char operation[512]; // operation to send to ors
+ std::ofstream adblogfile; // adb stream log file
+ std::string streamFn;
+ typedef void (twrpback::*ThreadPtr)(void);
+ typedef void* (*PThreadPtr)(void *);
+ void adbloginit(void); // setup adb log stream file
+ void close_backup_fds(); // close backup resources
+ void close_restore_fds(); // close restore resources
+ bool checkMD5Trailer(char adbReadStream[], uint64_t md5fnsize, twrpMD5* digest); // Check MD5 Trailer
+ void printErrMsg(std::string msg, int errNum); // print error msg to adb log
+};
+
+#endif // _TWRPBACK_HPP
diff --git a/applypatch/Android.mk b/applypatch/Android.mk
new file mode 100644
index 0000000..44112fd
--- /dev/null
+++ b/applypatch/Android.mk
@@ -0,0 +1,192 @@
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+# libapplypatch (static library)
+# ===============================
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := \
+ applypatch.cpp \
+ bspatch.cpp \
+ freecache.cpp \
+ imgpatch.cpp
+LOCAL_MODULE := libapplypatch
+LOCAL_MODULE_TAGS := optional
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH)/include \
+ $(commands_recovery_local_path)
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_STATIC_LIBRARIES := \
+ libotafault \
+ libbase \
+ libcrypto \
+ libbspatch \
+ libbz \
+ libz
+LOCAL_WHOLE_STATIC_LIBRARIES += libmtdutils
+LOCAL_CFLAGS := \
+ -DZLIB_CONST \
+ -Werror
+
+BOARD_RECOVERY_DEFINES := BOARD_BML_BOOT BOARD_BML_RECOVERY
+
+$(foreach board_define,$(BOARD_RECOVERY_DEFINES), \
+ $(if $($(board_define)), \
+ $(eval LOCAL_CFLAGS += -D$(board_define)=\"$($(board_define))\") \
+ ) \
+ )
+
+include $(BUILD_STATIC_LIBRARY)
+
+# libimgpatch (static library)
+# ===============================
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := \
+ bspatch.cpp \
+ imgpatch.cpp
+LOCAL_MODULE := libimgpatch
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH)/include \
+ $(commands_recovery_local_path)
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_STATIC_LIBRARIES := \
+ libcrypto \
+ libbspatch \
+ libbase \
+ libbz \
+ libz
+LOCAL_CFLAGS := \
+ -DZLIB_CONST \
+ -Werror
+include $(BUILD_STATIC_LIBRARY)
+
+# libimgpatch (host static library)
+# ===============================
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := \
+ bspatch.cpp \
+ imgpatch.cpp
+LOCAL_MODULE := libimgpatch
+LOCAL_MODULE_HOST_OS := linux
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH)/include \
+ $(commands_recovery_local_path)
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_STATIC_LIBRARIES := \
+ libcrypto \
+ libbspatch \
+ libbase \
+ libbz \
+ libz
+LOCAL_CFLAGS := \
+ -DZLIB_CONST \
+ -Werror
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+# libapplypatch_modes (static library)
+# ===============================
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := \
+ applypatch_modes.cpp
+LOCAL_MODULE := libapplypatch_modes
+LOCAL_C_INCLUDES := $(commands_recovery_local_path)
+LOCAL_STATIC_LIBRARIES := \
+ libapplypatch \
+ libbase \
+ libedify \
+ libcrypto
+LOCAL_CFLAGS := -Werror
+include $(BUILD_STATIC_LIBRARY)
+
+# applypatch (target executable)
+# ===============================
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := applypatch_main.cpp
+LOCAL_MODULE := applypatch
+LOCAL_C_INCLUDES := $(commands_recovery_local_path)
+LOCAL_STATIC_LIBRARIES := \
+ libapplypatch_modes \
+ libapplypatch \
+ libbase \
+ libedify \
+ libotafault \
+ libcrypto \
+ libbspatch \
+ libbz
+LOCAL_SHARED_LIBRARIES := \
+ libbase \
+ libz \
+ libcutils
+LOCAL_CFLAGS := -Werror
+include $(BUILD_EXECUTABLE)
+
+libimgdiff_src_files := imgdiff.cpp
+
+# libbsdiff is compiled with -D_FILE_OFFSET_BITS=64.
+libimgdiff_cflags := \
+ -Werror \
+ -D_FILE_OFFSET_BITS=64
+
+libimgdiff_static_libraries := \
+ libbsdiff \
+ libdivsufsort \
+ libdivsufsort64 \
+ libziparchive \
+ libutils \
+ liblog \
+ libbase \
+ libz
+
+# libimgdiff (static library)
+# ===============================
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := \
+ $(libimgdiff_src_files)
+LOCAL_MODULE := libimgdiff
+LOCAL_CFLAGS := \
+ $(libimgdiff_cflags)
+LOCAL_STATIC_LIBRARIES := \
+ $(libimgdiff_static_libraries)
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH)/include
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+include $(BUILD_STATIC_LIBRARY)
+
+# libimgdiff (host static library)
+# ===============================
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := \
+ $(libimgdiff_src_files)
+LOCAL_MODULE := libimgdiff
+LOCAL_CFLAGS := \
+ $(libimgdiff_cflags)
+LOCAL_STATIC_LIBRARIES := \
+ $(libimgdiff_static_libraries)
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH)/include
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+# imgdiff (host static executable)
+# ===============================
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := imgdiff_main.cpp
+LOCAL_MODULE := imgdiff
+LOCAL_CFLAGS := -Werror
+LOCAL_STATIC_LIBRARIES := \
+ libimgdiff \
+ $(libimgdiff_static_libraries) \
+ libbz
+include $(BUILD_HOST_EXECUTABLE)
diff --git a/applypatch/applypatch.cpp b/applypatch/applypatch.cpp
old mode 100644
new mode 100755
index adda697..206a3a3
--- a/applypatch/applypatch.cpp
+++ b/applypatch/applypatch.cpp
@@ -40,6 +40,9 @@
#include <android-base/unique_fd.h>
#include <openssl/sha.h>
+#include "bmlutils/bmlutils.h"
+#include "mtdutils/mtdutils.h"
+
#include "edify/expr.h"
#include "otautil/paths.h"
#include "otautil/print_sha1.h"
@@ -186,15 +189,8 @@
return false;
}
- unsigned char buffer[4096];
- start = len;
- for (size_t p = 0; p < len; p += sizeof(buffer)) {
- size_t to_read = len - p;
- if (to_read > sizeof(buffer)) {
- to_read = sizeof(buffer);
- }
+ const char* partition = pieces[1].c_str();
- if (!android::base::ReadFully(fd, buffer, to_read)) {
PLOG(ERROR) << "Failed to verify-read " << partition << " at " << p;
return false;
}
diff --git a/attr/Android.mk b/attr/Android.mk
new file mode 100755
index 0000000..121ae02
--- /dev/null
+++ b/attr/Android.mk
@@ -0,0 +1,22 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := listxattr.c
+LOCAL_CFLAGS := -c -W
+LOCAL_MODULE := listxattr
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/
+LOCAL_PACK_MODULE_RELOCATIONS := false
+
+ifneq ($(TARGET_ARCH), arm64)
+ ifneq ($(TARGET_ARCH), x86_64)
+ LOCAL_LDFLAGS += -Wl,-dynamic-linker,/system/bin/linker
+ else
+ LOCAL_LDFLAGS += -Wl,-dynamic-linker,/system/bin/linker64
+ endif
+else
+ LOCAL_LDFLAGS += -Wl,-dynamic-linker,/system/bin/linker64
+endif
+
+include $(BUILD_EXECUTABLE)
diff --git a/attr/listxattr.c b/attr/listxattr.c
new file mode 100644
index 0000000..1aa8e14
--- /dev/null
+++ b/attr/listxattr.c
@@ -0,0 +1,202 @@
+/****************************************************************************
+ | (C) Copyright 2008 Novell, Inc. All Rights Reserved.
+ |
+ | GPLv2: This program is free software; you can redistribute it
+ | and/or modify it under the terms of version 2 of the GNU General
+ | Public License as published by the Free Software Foundation.
+ |
+ | This program is distributed in the hope that it will be useful,
+ | but WITHOUT ANY WARRANTY; without even the implied warranty of
+ | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ | GNU General Public License for more details.
+ +-------------------------------------------------------------------------*/
+/*
+ * NOTE from Dees_Troy: modified source to display values along with xattr names
+ * Below code comments about usage are no longer accurate but came from the
+ * original source code from the chromium project and combine features of
+ * listxattr and getfattr:
+ * https://chromium.googlesource.com/chromiumos/platform/punybench/+/factory-1235.B/file.m/listxattr.c
+ * https://chromium.googlesource.com/chromiumos/platform/punybench/+/factory-1235.B/file.m/getxattr.c
+ */
+/*
+ * LISTXATTR(2) Linux Programmer's Manual LISTXATTR(2)
+ *
+ *
+ *
+ * NAME
+ * listxattr, llistxattr, flistxattr - list extended attribute names
+ *
+ * SYNOPSIS
+ * #include <sys/types.h>
+ * #include <attr/xattr.h>
+ *
+ * ssize_t listxattr (const char *path,
+ * char *list, size_t size);
+ * ssize_t llistxattr (const char *path,
+ * char *list, size_t size);
+ * ssize_t flistxattr (int filedes,
+ * char *list, size_t size);
+ *
+ * DESCRIPTION
+ * Extended attributes are name:value pairs associated with inodes
+ * (files, directories, symlinks, etc). They are extensions to the
+ * normal attributes which are associated with all inodes in the sys-
+ * tem (i.e. the stat(2) data). A complete overview of extended
+ * attributes concepts can be found in attr(5).
+ *
+ * listxattr retrieves the list of extended attribute names associated
+ * with the given path in the filesystem. The list is the set of
+ * (NULL-terminated) names, one after the other. Names of extended
+ * attributes to which the calling process does not have access may be
+ * omitted from the list. The length of the attribute name list is
+ * returned.
+ *
+ * llistxattr is identical to listxattr, except in the case of a sym-
+ * bolic link, where the list of names of extended attributes associ-
+ * ated with the link itself is retrieved, not the file that it refers
+ * to.
+ *
+ * flistxattr is identical to listxattr, only the open file pointed to
+ * by filedes (as returned by open(2)) is interrogated in place of
+ * path.
+ *
+ * A single extended attribute name is a simple NULL-terminated
+ * string. The name includes a namespace prefix; there may be sev-
+ * eral, disjoint namespaces associated with an individual inode.
+ *
+ * An empty buffer of size zero can be passed into these calls to
+ * return the current size of the list of extended attribute names,
+ * which can be used to estimate the size of a buffer which is suffi-
+ * ciently large to hold the list of names.
+ *
+ * EXAMPLES
+ * The list of names is returned as an unordered array of NULL-termi-
+ * nated character strings (attribute names are separated by NULL
+ * characters), like this:
+ * user.name1\0system.name1\0user.name2\0
+ *
+ * Filesystems like ext2, ext3 and XFS which implement POSIX ACLs
+ * using extended attributes, might return a list like this:
+ * system.posix_acl_access\0system.posix_acl_default\0
+ *
+ * RETURN VALUE
+ * On success, a positive number is returned indicating the size of
+ * the extended attribute name list. On failure, -1 is returned and
+ * errno is set appropriately.
+ *
+ * If the size of the list buffer is too small to hold the result,
+ * errno is set to ERANGE.
+ *
+ * If extended attributes are not supported by the filesystem, or are
+ * disabled, errno is set to ENOTSUP.
+ *
+ * The errors documented for the stat(2) system call are also applica-
+ * ble here.
+ *
+ * AUTHORS
+ * Andreas Gruenbacher, <a.gruenbacher@computer.org> and the SGI XFS
+ * development team, <linux-xfs@oss.sgi.com>. Please send any bug
+ * reports or comments to these addresses.
+ *
+ * SEE ALSO
+ * getfattr(1), setfattr(1), getxattr(2), open(2), removexattr(2),
+ * setxattr(2), stat(2), attr(5)
+ *
+ *
+ *
+ * Dec 2001 Extended Attributes LISTXATTR(2)
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/xattr.h>
+//#include <eprintf.h>
+//#include <puny.h>
+
+/* dumpmem: dumps an n byte area of memory to screen */
+void dumpmem (const void *mem, unsigned int n)
+{
+ const char *c = mem;
+ unsigned i;
+ int all_text = 1;
+ if (n == 0) {
+ printf("<empty>");
+ return;
+ }
+ for (i = 0; i < n - 1; i++, c++) {
+ if (!isprint(*c)) {
+ all_text = 0;
+ break;
+ }
+ }
+ c = mem;
+ if (all_text) {
+ for (i = 0; i < n - 1; i++, c++) {
+ putchar(*c);
+ }
+ return;
+ } else {
+ char hex[(n * 2) + 1];
+ for(i = 0; i < n; i++, c++)
+ sprintf(hex + (i * 2), "%02X", *c);
+ hex[n] = 0;
+ printf("0x%s", hex);
+ return;
+ }
+}
+
+void dump_list (char *file, char *list, ssize_t size)
+{
+ int c;
+ int i;
+ int first = 1;
+ int j = 0;
+ char xattr[1024];
+ char value[1024];
+ ssize_t size2;
+ for (i = 0; i < size; i++) {
+ c = list[i];
+ if (c) {
+ if (first) {
+ putchar('\t');
+ first = 0;
+ j = 0;
+ }
+ putchar(c);
+ xattr[j++] = list[i];
+ } else {
+ xattr[j] = '\0';
+ size2 = getxattr(file, xattr, value, sizeof(value));
+ if (size2 < 0) {
+ printf("file=%s xattr=%s returned:", file, xattr);
+ } else {
+ putchar('=');
+ dumpmem(value, size2);
+ }
+ putchar('\n');
+ first = 1;
+ }
+ }
+}
+void usage (void)
+{
+ printf("listxattr <file>");
+}
+char List[1<<17];
+int main (int argc, char *argv[])
+{
+ ssize_t size;
+ if (argc < 2) {
+ usage();
+ exit(2);
+ }
+ size = listxattr(argv[1], List, sizeof(List));
+ if (size == -1) {
+ perror(argv[1]);
+ exit(2);
+ }
+ printf("xattrs for %s:\n", argv[1]);
+ dump_list(argv[1], List, size);
+ return 0;
+}
diff --git a/bmlutils/Android.mk b/bmlutils/Android.mk
new file mode 100644
index 0000000..9cbada0
--- /dev/null
+++ b/bmlutils/Android.mk
@@ -0,0 +1,34 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+BOARD_RECOVERY_DEFINES := BOARD_BML_BOOT BOARD_BML_RECOVERY
+LOCAL_STATIC_LIBRARY := libcrecovery
+
+$(foreach board_define,$(BOARD_RECOVERY_DEFINES), \
+ $(if $($(board_define)), \
+ $(eval LOCAL_CFLAGS += -D$(board_define)=\"$($(board_define))\") \
+ ) \
+ )
+
+LOCAL_SRC_FILES := bmlutils.c
+LOCAL_MODULE := libbmlutils
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_STATIC_LIBRARY)
+
+#Added for building TWRP dynamic:
+include $(CLEAR_VARS)
+
+BOARD_RECOVERY_DEFINES := BOARD_BML_BOOT BOARD_BML_RECOVERY
+LOCAL_SHARED_LIBRARIES := libcrecovery
+
+$(foreach board_define,$(BOARD_RECOVERY_DEFINES), \
+ $(if $($(board_define)), \
+ $(eval LOCAL_CFLAGS += -D$(board_define)=\"$($(board_define))\") \
+ ) \
+ )
+
+LOCAL_SRC_FILES := bmlutils.c
+LOCAL_MODULE := libbmlutils
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_SHARED_LIBRARY)
diff --git a/bmlutils/bmlutils.c b/bmlutils/bmlutils.c
new file mode 100755
index 0000000..382c0a5
--- /dev/null
+++ b/bmlutils/bmlutils.c
@@ -0,0 +1,197 @@
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <limits.h>
+#include <linux/input.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/reboot.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <sys/wait.h>
+#include <sys/limits.h>
+#include <dirent.h>
+#include <sys/stat.h>
+
+#include <signal.h>
+#include <sys/wait.h>
+
+#include <bmlutils.h>
+
+#include "../libcrecovery/common.h"
+
+static int restore_internal(const char* bml, const char* filename)
+{
+ char buf[4096];
+ int dstfd, srcfd, bytes_read, total_read = 0;
+ if (filename == NULL)
+ srcfd = 0;
+ else {
+ srcfd = open(filename, O_RDONLY | O_LARGEFILE);
+ if (srcfd < 0)
+ return 2;
+ }
+ dstfd = open(bml, O_RDWR | O_LARGEFILE);
+ if (dstfd < 0)
+ return 3;
+ if (ioctl(dstfd, BML_UNLOCK_ALL, 0))
+ return 4;
+ do {
+ total_read += bytes_read = read(srcfd, buf, 4096);
+ if (!bytes_read)
+ break;
+ if (bytes_read < 4096)
+ memset(&buf[bytes_read], 0, 4096 - bytes_read);
+ if (write(dstfd, buf, 4096) < 4096)
+ return 5;
+ } while(bytes_read == 4096);
+
+ close(dstfd);
+ close(srcfd);
+
+ return 0;
+}
+
+int cmd_bml_restore_raw_partition(const char *partition, const char *filename)
+{
+ if (strcmp(partition, "boot") != 0 && strcmp(partition, "recovery") != 0 && strcmp(partition, "recoveryonly") != 0 && partition[0] != '/')
+ return 6;
+
+ int ret = -1;
+ if (strcmp(partition, "recoveryonly") != 0) {
+ // always restore boot, regardless of whether recovery or boot is flashed.
+ // this is because boot and recovery are the same on some samsung phones.
+ // unless of course, recoveryonly is explictly chosen (bml8)
+ ret = restore_internal(BOARD_BML_BOOT, filename);
+ if (ret != 0)
+ return ret;
+ }
+
+ if (strcmp(partition, "recovery") == 0 || strcmp(partition, "recoveryonly") == 0)
+ ret = restore_internal(BOARD_BML_RECOVERY, filename);
+
+ // support explicitly provided device paths
+ if (partition[0] == '/')
+ ret = restore_internal(partition, filename);
+ return ret;
+}
+
+int cmd_bml_backup_raw_partition(const char *partition, const char *out_file)
+{
+ const char* bml;
+ if (strcmp("boot", partition) == 0)
+ bml = BOARD_BML_BOOT;
+ else if (strcmp("recovery", partition) == 0)
+ bml = BOARD_BML_RECOVERY;
+ else if (partition[0] == '/') {
+ // support explicitly provided device paths
+ bml = partition;
+ }
+ else {
+ printf("Invalid partition.\n");
+ return -1;
+ }
+
+ int ch;
+ FILE *in;
+ FILE *out;
+ char buf[512];
+ unsigned sz = 0;
+ unsigned i;
+ int ret = -1;
+ const char *in_file = bml;
+
+ in = fopen ( in_file, "r" );
+ if (in == NULL)
+ goto ERROR3;
+
+ out = fopen ( out_file, "w" );
+ if (out == NULL)
+ goto ERROR2;
+
+ fseek(in, 0L, SEEK_END);
+ sz = ftell(in);
+ fseek(in, 0L, SEEK_SET);
+
+ if (sz % 512)
+ {
+ while ( ( ch = fgetc ( in ) ) != EOF )
+ fputc ( ch, out );
+ }
+ else
+ {
+ for (i=0; i< (sz/512); i++)
+ {
+ if ((fread(buf, 512, 1, in)) != 1)
+ goto ERROR1;
+ if ((fwrite(buf, 512, 1, out)) != 1)
+ goto ERROR1;
+ }
+ }
+
+ fsync(fileno(out));
+ ret = 0;
+ERROR1:
+ fclose ( out );
+ERROR2:
+ fclose ( in );
+ERROR3:
+ return ret;
+}
+
+int cmd_bml_erase_raw_partition(const char *partition __unused)
+{
+ // TODO: implement raw wipe
+ return 0;
+}
+
+int cmd_bml_erase_partition(const char *partition __unused, const char *filesystem __unused)
+{
+ return -1;
+}
+
+int cmd_bml_mount_partition(const char *partition __unused, const char *mount_point __unused, const char *filesystem __unused, int read_only __unused)
+{
+ return -1;
+}
+
+int cmd_bml_get_partition_device(const char *partition __unused, char *device __unused)
+{
+ return -1;
+}
+
+int format_rfs_device (const char *device, const char *path) {
+ const char *fatsize = "32";
+ const char *sectorsize = "1";
+
+ if (strcmp(path, "/datadata") == 0 || strcmp(path, "/cache") == 0) {
+ fatsize = "16";
+ }
+
+ // Just in case /data sector size needs to be altered
+ else if (strcmp(path, "/data") == 0 ) {
+ sectorsize = "1";
+ }
+
+ // dump 10KB of zeros to partition before format due to fat.format bug
+ char cmd[PATH_MAX];
+
+ sprintf(cmd, "/system/bin/dd if=/dev/zero of=%s bs=4096 count=10", device);
+ if(__system(cmd)) {
+ printf("failure while zeroing rfs partition.\n");
+ return -1;
+ }
+
+ // Run fat.format
+ sprintf(cmd, "/system/bin/fat.format -F %s -S 4096 -s %s %s", fatsize, sectorsize, device);
+ if(__system(cmd)) {
+ printf("failure while running fat.format\n");
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/bmlutils/bmlutils.h b/bmlutils/bmlutils.h
new file mode 100644
index 0000000..1e85f08
--- /dev/null
+++ b/bmlutils/bmlutils.h
@@ -0,0 +1,16 @@
+#ifndef BMLUTILS_H_
+#define BMLUTILS_H_
+
+int format_rfs_device (const char *device, const char *path);
+
+#define BML_UNLOCK_ALL 0x8A29 ///< unlock all partition RO -> RW
+
+#ifndef BOARD_BML_BOOT
+#define BOARD_BML_BOOT "/dev/block/bml7"
+#endif
+
+#ifndef BOARD_BML_RECOVERY
+#define BOARD_BML_RECOVERY "/dev/block/bml8"
+#endif
+
+#endif // BMLUTILS_H_
diff --git a/bootloader_message/bootloader_message.cpp b/bootloader_message/bootloader_message.cpp
old mode 100644
new mode 100755
diff --git a/crypto/ext4crypt/Android.mk b/crypto/ext4crypt/Android.mk
new file mode 100755
index 0000000..8b1372b
--- /dev/null
+++ b/crypto/ext4crypt/Android.mk
@@ -0,0 +1,111 @@
+LOCAL_PATH := $(call my-dir)
+ifeq ($(TW_INCLUDE_CRYPTO), true)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libe4crypt
+LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS :=
+LOCAL_SRC_FILES := Decrypt.cpp ScryptParameters.cpp Utils.cpp HashPassword.cpp ext4_crypt.cpp
+LOCAL_SHARED_LIBRARIES := libselinux libc libc++ libext4_utils libbase libcrypto libcutils libkeymaster_messages libhardware libprotobuf-cpp-lite
+LOCAL_STATIC_LIBRARIES := libscrypt_static
+LOCAL_C_INCLUDES := system/extras/ext4_utils \
+ system/extras/ext4_utils/include/ext4_utils \
+ external/scrypt/lib/crypto \
+ system/security/keystore/include \
+ hardware/libhardware/include/hardware \
+ system/security/softkeymaster/include/keymaster \
+ system/keymaster/include
+
+ifneq ($(wildcard hardware/libhardware/include/hardware/keymaster0.h),)
+ LOCAL_CFLAGS += -DTW_CRYPTO_HAVE_KEYMASTERX
+ LOCAL_C_INCLUDES += external/boringssl/src/include
+endif
+ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 26; echo $$?),0)
+ #8.0 or higher
+ LOCAL_CFLAGS += -DHAVE_GATEKEEPER1
+ ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 29; echo $$?),0)
+ LOCAL_SHARED_LIBRARIES += android.hardware.confirmationui@1.0
+ endif
+ LOCAL_SHARED_LIBRARIES += android.hardware.keymaster@3.0 libkeystore_binder libhidlbase libutils libbinder android.hardware.gatekeeper@1.0
+ ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 28; echo $$?),0)
+ #9.0 rules
+ LOCAL_CFLAGS += -DUSE_KEYSTORAGE_4 -Wno-unused-variable -Wno-sign-compare -Wno-unused-parameter -Wno-comment
+ LOCAL_SRC_FILES += Ext4CryptPie.cpp Keymaster4.cpp KeyStorage4.cpp KeyUtil.cpp MetadataCrypt.cpp KeyBuffer.cpp
+ LOCAL_SHARED_LIBRARIES += android.hardware.keymaster@4.0 libkeymaster4support
+ LOCAL_SHARED_LIBRARIES += android.hardware.gatekeeper@1.0 libkeystore_parcelables libkeystore_aidl
+ LOCAL_CFLAGS += -DHAVE_SYNTH_PWD_SUPPORT
+ LOCAL_SRC_FILES += Weaver1.cpp
+ LOCAL_SHARED_LIBRARIES += android.hardware.weaver@1.0
+ LOCAL_CFLAGS += -DHAVE_LIBKEYUTILS
+ LOCAL_SHARED_LIBRARIES += libkeyutils
+ else
+ #8.0 rules
+ LOCAL_CFLAGS += -DUSE_KEYSTORAGE_3
+ LOCAL_SRC_FILES += Ext4Crypt.cpp Keymaster3.cpp KeyStorage3.cpp
+ ifneq ($(wildcard hardware/interfaces/weaver/Android.bp),)
+ #only present in some 8.0 trees and should be in all 8.1 trees
+ LOCAL_CFLAGS += -DHAVE_SYNTH_PWD_SUPPORT
+ LOCAL_SRC_FILES += Weaver1.cpp
+ LOCAL_SHARED_LIBRARIES += android.hardware.weaver@1.0
+ endif
+ ifneq ($(wildcard system/core/libkeyutils/Android.bp),)
+ #only present in some 8.0 trees and should be in all 8.1 trees
+ LOCAL_CFLAGS += -DHAVE_LIBKEYUTILS
+ LOCAL_SHARED_LIBRARIES += libkeyutils
+ endif
+ endif
+ ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 28; echo $$?),0)
+ LOCAL_REQUIRED_MODULES := keystore_auth
+ else
+ LOCAL_ADDITIONAL_DEPENDENCIES := keystore_auth
+ endif
+else
+ #7.x rules
+ LOCAL_SRC_FILES += Ext4Crypt.cpp Keymaster.cpp KeyStorage.cpp
+endif
+ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 28; echo $$?),0)
+ LOCAL_SHARED_LIBRARIES += libsoftkeymaster
+endif
+
+include $(BUILD_SHARED_LIBRARY)
+
+
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := twrpfbe
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/
+LOCAL_SRC_FILES := main.cpp
+LOCAL_SHARED_LIBRARIES := libe4crypt
+
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := e4policyget
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/
+LOCAL_SRC_FILES := e4policyget.cpp
+LOCAL_SHARED_LIBRARIES := libe4crypt
+LOCAL_LDFLAGS += -Wl,-dynamic-linker,/system/bin/linker64
+
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := keystore_auth
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/
+LOCAL_SRC_FILES := keystore_auth.cpp
+LOCAL_SHARED_LIBRARIES := libc libkeystore_binder libutils libbinder liblog
+ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 28; echo $$?),0)
+ #9.0
+ LOCAL_CFLAGS += -DUSE_SECURITY_NAMESPACE
+ LOCAL_SHARED_LIBRARIES += libkeystore_aidl
+endif
+LOCAL_LDFLAGS += -Wl,-dynamic-linker,/system/bin/linker64
+
+include $(BUILD_EXECUTABLE)
+
+endif
diff --git a/crypto/ext4crypt/Decrypt.cpp b/crypto/ext4crypt/Decrypt.cpp
new file mode 100755
index 0000000..d486aa4
--- /dev/null
+++ b/crypto/ext4crypt/Decrypt.cpp
@@ -0,0 +1,1359 @@
+/*
+ * Copyright (C) 2016 The Team Win Recovery Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Decrypt.h"
+#ifdef USE_KEYSTORAGE_4
+#include "Ext4CryptPie.h"
+#else
+#include "Ext4Crypt.h"
+#endif
+
+#include <map>
+#include <string>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#ifndef HAVE_LIBKEYUTILS
+#include "key_control.h"
+#else
+#include <keyutils.h>
+#endif
+
+#ifdef HAVE_SYNTH_PWD_SUPPORT
+#include "Weaver1.h"
+#include "cutils/properties.h"
+
+#include <openssl/sha.h>
+#include <openssl/aes.h>
+#include <openssl/evp.h>
+#include <openssl/rand.h>
+
+#include <dirent.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/types.h>
+#include <fstream>
+
+#include "ext4_crypt.h"
+
+#ifdef USE_KEYSTORAGE_4
+#include <android/hardware/confirmationui/1.0/types.h>
+#include <android/security/BnConfirmationPromptCallback.h>
+#include <android/security/keystore/IKeystoreService.h>
+#else
+#include <keystore/IKeystoreService.h>
+#include <keystore/authorization_set.h>
+#endif
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+
+#include <keystore/keystore.h>
+
+#include <algorithm>
+extern "C" {
+#include "crypto_scrypt.h"
+}
+#else
+#include "ext4_crypt.h"
+#endif //ifdef HAVE_SYNTH_PWD_SUPPORT
+
+#ifdef HAVE_GATEKEEPER1
+#include <android/hardware/gatekeeper/1.0/IGatekeeper.h>
+#else
+#include <hardware/gatekeeper.h>
+#endif
+#include "HashPassword.h"
+
+#include <android-base/file.h>
+
+#ifdef USE_KEYSTORAGE_4
+using android::security::keystore::IKeystoreService;
+#endif
+
+// Store main DE raw ref / policy
+extern std::string de_raw_ref;
+extern std::map<userid_t, std::string> s_de_key_raw_refs;
+extern std::map<userid_t, std::string> s_ce_key_raw_refs;
+
+static bool lookup_ref_key_internal(std::map<userid_t, std::string>& key_map, const char* policy, userid_t* user_id) {
+ for (std::map<userid_t, std::string>::iterator it=key_map.begin(); it!=key_map.end(); ++it) {
+ if (strncmp(it->second.c_str(), policy, it->second.size()) == 0) {
+ *user_id = it->first;
+ return true;
+ }
+ }
+ return false;
+}
+
+extern "C" bool lookup_ref_key(const char* policy, char* policy_type) {
+ userid_t user_id = 0;
+ if (strncmp(de_raw_ref.c_str(), policy, de_raw_ref.size()) == 0) {
+ strcpy(policy_type, "1DK");
+ return true;
+ }
+ if (!lookup_ref_key_internal(s_de_key_raw_refs, policy, &user_id)) {
+ if (!lookup_ref_key_internal(s_ce_key_raw_refs, policy, &user_id)) {
+ return false;
+ } else
+ sprintf(policy_type, "1CE%d", user_id);
+ } else
+ sprintf(policy_type, "1DE%d", user_id);
+ return true;
+}
+
+extern "C" bool lookup_ref_tar(const char* policy_type, char* policy) {
+ if (strncmp(policy_type, "1", 1) != 0) {
+ printf("Unexpected version %c\n", policy_type[0]);
+ return false;
+ }
+ const char* ptr = policy_type + 1; // skip past the version number
+ if (strncmp(ptr, "DK", 2) == 0) {
+ strncpy(policy, de_raw_ref.data(), de_raw_ref.size());
+ return true;
+ }
+ userid_t user_id = atoi(ptr + 2);
+ std::string raw_ref;
+ if (*ptr == 'D') {
+ if (lookup_key_ref(s_de_key_raw_refs, user_id, &raw_ref)) {
+ strncpy(policy, raw_ref.data(), raw_ref.size());
+ } else
+ return false;
+ } else if (*ptr == 'C') {
+ if (lookup_key_ref(s_ce_key_raw_refs, user_id, &raw_ref)) {
+ strncpy(policy, raw_ref.data(), raw_ref.size());
+ } else
+ return false;
+ } else {
+ printf("unknown policy type '%s'\n", policy_type);
+ return false;
+ }
+ return true;
+}
+
+#ifndef HAVE_GATEKEEPER1
+int gatekeeper_device_initialize(gatekeeper_device_t **dev) {
+ int ret;
+ const hw_module_t *mod;
+ ret = hw_get_module_by_class(GATEKEEPER_HARDWARE_MODULE_ID, NULL, &mod);
+
+ if (ret!=0) {
+ printf("failed to get hw module\n");
+ return ret;
+ }
+
+ ret = gatekeeper_open(mod, dev);
+
+ if (ret!=0)
+ printf("failed to open gatekeeper\n");
+ return ret;
+}
+#endif //ifndef HAVE_GATEKEEPER1
+
+bool Decrypt_DE() {
+ if (!e4crypt_initialize_global_de()) { // this deals with the overarching device encryption
+ printf("e4crypt_initialize_global_de returned fail\n");
+ return false;
+ }
+ if (!e4crypt_init_user0()) {
+ printf("e4crypt_init_user0 returned fail\n");
+ return false;
+ }
+ return true;
+}
+
+#ifdef HAVE_SYNTH_PWD_SUPPORT
+// Crappy functions for debugging, please ignore unless you need to debug
+/*void output_hex(const std::string& in) {
+ const char *buf = in.data();
+ char hex[in.size() * 2 + 1];
+ unsigned int index;
+ for (index = 0; index < in.size(); index++)
+ sprintf(&hex[2 * index], "%02X", buf[index]);
+ printf("%s", hex);
+}
+
+void output_hex(const char* buf, const int size) {
+ char hex[size * 2 + 1];
+ int index;
+ for (index = 0; index < size; index++)
+ sprintf(&hex[2 * index], "%02X", buf[index]);
+ printf("%s", hex);
+}
+
+void output_hex(const unsigned char* buf, const int size) {
+ char hex[size * 2 + 1];
+ int index;
+ for (index = 0; index < size; index++)
+ sprintf(&hex[2 * index], "%02X", buf[index]);
+ printf("%s", hex);
+}
+
+void output_hex(std::vector<uint8_t>* vec) {
+ char hex[3];
+ unsigned int index;
+ for (index = 0; index < vec->size(); index++) {
+ sprintf(&hex[0], "%02X", vec->at(index));
+ printf("%s", hex);
+ }
+}*/
+
+/* An alternative is to use:
+ * sqlite3 /data/system/locksettings.db "SELECT value FROM locksettings WHERE name='sp-handle' AND user=0;"
+ * but we really don't want to include the 1.1MB libsqlite in TWRP. We scan the spblob folder for the
+ * password data file (*.pwd) and get the handle from the filename instead. This is a replacement for
+ * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/LockSettingsService.java#2017
+ * We never use this data as an actual long. We always use it as a string. */
+bool Find_Handle(const std::string& spblob_path, std::string& handle_str) {
+ DIR* dir = opendir(spblob_path.c_str());
+ if (!dir) {
+ printf("Error opening '%s'\n", spblob_path.c_str());
+ return false;
+ }
+
+ struct dirent* de = 0;
+
+ while ((de = readdir(dir)) != 0) {
+ if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
+ continue;
+ size_t len = strlen(de->d_name);
+ if (len <= 4)
+ continue;
+ char* p = de->d_name;
+ p += len - 4;
+ if (strncmp(p, ".pwd", 4) == 0) {
+ handle_str = de->d_name;
+ handle_str = handle_str.substr(0, len - 4);
+ //*handle = strtoull(handle_str.c_str(), 0 , 16);
+ closedir(dir);
+ return true;
+ }
+ }
+ closedir(dir);
+ return false;
+}
+
+/* This is the structure of the data in the password data (*.pwd) file which the structure can be found
+ * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#187 */
+struct password_data_struct {
+ int password_type;
+ unsigned char scryptN;
+ unsigned char scryptR;
+ unsigned char scryptP;
+ int salt_len;
+ void* salt;
+ int handle_len;
+ void* password_handle;
+};
+
+/* C++ replacement for
+ * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#764 */
+bool Get_Password_Data(const std::string& spblob_path, const std::string& handle_str, password_data_struct *pwd) {
+ std::string pwd_file = spblob_path + handle_str + ".pwd";
+ std::string pwd_data;
+ if (!android::base::ReadFileToString(pwd_file, &pwd_data)) {
+ printf("Failed to read '%s'\n", pwd_file.c_str());
+ return false;
+ }
+ //output_hex(pwd_data.data(), pwd_data.size());printf("\n");
+ const int* intptr = (const int*)pwd_data.data();
+ pwd->password_type = *intptr;
+ endianswap(&pwd->password_type);
+ //printf("password type %i\n", pwd->password_type); // 2 was PIN, 1 for pattern, 2 also for password, -1 for default password
+ const unsigned char* byteptr = (const unsigned char*)pwd_data.data() + sizeof(int);
+ pwd->scryptN = *byteptr;
+ byteptr++;
+ pwd->scryptR = *byteptr;
+ byteptr++;
+ pwd->scryptP = *byteptr;
+ byteptr++;
+ intptr = (const int*)byteptr;
+ pwd->salt_len = *intptr;
+ endianswap(&pwd->salt_len);
+ if (pwd->salt_len != 0) {
+ pwd->salt = malloc(pwd->salt_len);
+ if (!pwd->salt) {
+ printf("Get_Password_Data malloc salt\n");
+ return false;
+ }
+ memcpy(pwd->salt, intptr + 1, pwd->salt_len);
+ intptr++;
+ byteptr = (const unsigned char*)intptr;
+ byteptr += pwd->salt_len;
+ } else {
+ printf("Get_Password_Data salt_len is 0\n");
+ return false;
+ }
+ intptr = (const int*)byteptr;
+ pwd->handle_len = *intptr;
+ endianswap(&pwd->handle_len);
+ if (pwd->handle_len != 0) {
+ pwd->password_handle = malloc(pwd->handle_len);
+ if (!pwd->password_handle) {
+ printf("Get_Password_Data malloc password_handle\n");
+ return false;
+ }
+ memcpy(pwd->password_handle, intptr + 1, pwd->handle_len);
+ } else {
+ printf("Get_Password_Data handle_len is 0\n");
+ // Not an error if using weaver
+ }
+ return true;
+}
+
+/* C++ replacement for
+ * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#765
+ * called here
+ * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#1050 */
+bool Get_Password_Token(const password_data_struct *pwd, const std::string& Password, unsigned char* password_token) {
+ if (!password_token) {
+ printf("password_token is null\n");
+ return false;
+ }
+ unsigned int N = 1 << pwd->scryptN;
+ unsigned int r = 1 << pwd->scryptR;
+ unsigned int p = 1 << pwd->scryptP;
+ //printf("N %i r %i p %i\n", N, r, p);
+ int ret = crypto_scrypt(reinterpret_cast<const uint8_t*>(Password.data()), Password.size(),
+ reinterpret_cast<const uint8_t*>(pwd->salt), pwd->salt_len,
+ N, r, p,
+ password_token, 32);
+ if (ret != 0) {
+ printf("scrypt error\n");
+ return false;
+ }
+ return true;
+}
+
+// Data structure for the *.weaver file, see Get_Weaver_Data below
+struct weaver_data_struct {
+ unsigned char version;
+ int slot;
+};
+
+/* C++ replacement for
+ * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#501
+ * called here
+ * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#768 */
+bool Get_Weaver_Data(const std::string& spblob_path, const std::string& handle_str, weaver_data_struct *wd) {
+ std::string weaver_file = spblob_path + handle_str + ".weaver";
+ std::string weaver_data;
+ if (!android::base::ReadFileToString(weaver_file, &weaver_data)) {
+ printf("Failed to read '%s'\n", weaver_file.c_str());
+ return false;
+ }
+ //output_hex(weaver_data.data(), weaver_data.size());printf("\n");
+ const unsigned char* byteptr = (const unsigned char*)weaver_data.data();
+ wd->version = *byteptr;
+ //printf("weaver version %i\n", wd->version);
+ const int* intptr = (const int*)weaver_data.data() + sizeof(unsigned char);
+ wd->slot = *intptr;
+ //endianswap(&wd->slot); not needed
+ //printf("weaver slot %i\n", wd->slot);
+ return true;
+}
+
+namespace android {
+
+// On Android 8.0 for some reason init can't seem to completely stop keystore
+// so we have to kill it too if it doesn't die on its own.
+static void kill_keystore() {
+ DIR* dir = opendir("/proc");
+ if (dir) {
+ struct dirent* de = 0;
+
+ while ((de = readdir(dir)) != 0) {
+ if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
+ continue;
+
+ int pid = -1;
+ int ret = sscanf(de->d_name, "%d", &pid);
+
+ if (ret == 1) {
+ char cmdpath[PATH_MAX];
+ sprintf(cmdpath, "/proc/%d/cmdline", pid);
+
+ FILE* file = fopen(cmdpath, "r");
+ size_t task_size = PATH_MAX;
+ char task[PATH_MAX];
+ char* p = task;
+ if (getline(&p, &task_size, file) > 0) {
+ if (strstr(task, "keystore") != 0) {
+ printf("keystore pid %d found, sending kill.\n", pid);
+ kill(pid, SIGINT);
+ usleep(5000);
+ kill(pid, SIGKILL);
+ }
+ }
+ fclose(file);
+ }
+ }
+ closedir(dir);
+ }
+}
+
+// The keystore holds a file open on /data so we have to stop / kill it
+// if we want to be able to unmount /data for things like formatting.
+static void stop_keystore() {
+ printf("Stopping keystore...\n");
+ property_set("ctl.stop", "keystore");
+ usleep(5000);
+ kill_keystore();
+}
+
+/* These next 2 functions try to get the keystore service 50 times because
+ * the keystore is not always ready when TWRP boots */
+sp<IBinder> getKeystoreBinder() {
+ sp<IServiceManager> sm = defaultServiceManager();
+ return sm->getService(String16("android.security.keystore"));
+}
+
+sp<IBinder> getKeystoreBinderRetry() {
+ printf("Starting keystore...\n");
+ property_set("ctl.start", "keystore");
+ int retry_count = 50;
+ sp<IBinder> binder = getKeystoreBinder();
+ while (binder == NULL && retry_count) {
+ printf("Waiting for keystore service... %i\n", retry_count--);
+ sleep(1);
+ binder = getKeystoreBinder();
+ }
+ return binder;
+}
+
+namespace keystore {
+
+#define SYNTHETIC_PASSWORD_VERSION_V1 1
+#define SYNTHETIC_PASSWORD_VERSION_V2 2
+#define SYNTHETIC_PASSWORD_VERSION_V3 3
+#define SYNTHETIC_PASSWORD_PASSWORD_BASED 0
+#define SYNTHETIC_PASSWORD_KEY_PREFIX "USRSKEY_synthetic_password_"
+#define USR_PRIVATE_KEY_PREFIX "USRPKEY_synthetic_password_"
+
+static std::string mKey_Prefix;
+
+/* The keystore alias subid is sometimes the same as the handle, but not always.
+ * In the case of handle 0c5303fd2010fe29, the alias subid used c5303fd2010fe29
+ * without the leading 0. We could try to parse the data from a previous
+ * keystore request, but I think this is an easier solution because there
+ * is little to no documentation on the format of data we get back from
+ * the keystore in this instance. We also want to copy everything to a temp
+ * folder so that any key upgrades that might take place do not actually
+ * upgrade the keys on the data partition. We rename all 1000 uid files to 0
+ * to pass the keystore permission checks. */
+bool Find_Keystore_Alias_SubID_And_Prep_Files(const userid_t user_id, std::string& keystoreid, const std::string& handle_str) {
+ char path_c[PATH_MAX];
+ sprintf(path_c, "/data/misc/keystore/user_%d", user_id);
+ char user_dir[PATH_MAX];
+ sprintf(user_dir, "user_%d", user_id);
+ std::string source_path = "/data/misc/keystore/";
+ source_path += user_dir;
+ std::string handle_sub = handle_str;
+ while (handle_sub.substr(0,1) == "0") {
+ std::string temp = handle_sub.substr(1);
+ handle_sub = temp;
+ }
+ mKey_Prefix = "";
+
+ mkdir("/tmp/misc", 0755);
+ mkdir("/tmp/misc/keystore", 0755);
+ std::string destination_path = "/tmp/misc/keystore/";
+ destination_path += user_dir;
+ if (mkdir(destination_path.c_str(), 0755) && errno != EEXIST) {
+ printf("failed to mkdir '%s' %s\n", destination_path.c_str(), strerror(errno));
+ return false;
+ }
+ destination_path += "/";
+
+ DIR* dir = opendir(source_path.c_str());
+ if (!dir) {
+ printf("Error opening '%s'\n", source_path.c_str());
+ return false;
+ }
+ source_path += "/";
+
+ struct dirent* de = 0;
+ size_t prefix_len = strlen(SYNTHETIC_PASSWORD_KEY_PREFIX);
+ bool found_subid = false;
+ bool has_pkey = false; // PKEY has priority over SKEY
+
+ while ((de = readdir(dir)) != 0) {
+ if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
+ continue;
+ if (!found_subid) {
+ size_t len = strlen(de->d_name);
+ if (len <= prefix_len)
+ continue;
+ if (strstr(de->d_name, SYNTHETIC_PASSWORD_KEY_PREFIX) && !has_pkey)
+ mKey_Prefix = SYNTHETIC_PASSWORD_KEY_PREFIX;
+ else if (strstr(de->d_name, USR_PRIVATE_KEY_PREFIX)) {
+ mKey_Prefix = USR_PRIVATE_KEY_PREFIX;
+ has_pkey = true;
+ } else
+ continue;
+ if (strstr(de->d_name, handle_sub.c_str())) {
+ keystoreid = handle_sub;
+ printf("keystoreid matched handle_sub: '%s'\n", keystoreid.c_str());
+ found_subid = true;
+ } else {
+ std::string file = de->d_name;
+ std::size_t found = file.find_last_of("_");
+ if (found != std::string::npos) {
+ keystoreid = file.substr(found + 1);
+ printf("possible keystoreid: '%s'\n", keystoreid.c_str());
+ //found_subid = true; // we'll keep going in hopes that we find a pkey or a match to the handle_sub
+ }
+ }
+ }
+ std::string src = source_path;
+ src += de->d_name;
+ std::ifstream srcif(src.c_str(), std::ios::binary);
+ std::string dst = destination_path;
+ dst += de->d_name;
+ std::size_t source_uid = dst.find("1000");
+ if (source_uid != std::string::npos)
+ dst.replace(source_uid, 4, "0");
+ std::ofstream dstof(dst.c_str(), std::ios::binary);
+ printf("copying '%s' to '%s'\n", src.c_str(), dst.c_str());
+ dstof << srcif.rdbuf();
+ srcif.close();
+ dstof.close();
+ }
+ closedir(dir);
+ if (!found_subid && !mKey_Prefix.empty() && !keystoreid.empty())
+ found_subid = true;
+ return found_subid;
+}
+
+/* C++ replacement for function of the same name
+ * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#867
+ * returning an empty string indicates an error */
+std::string unwrapSyntheticPasswordBlob(const std::string& spblob_path, const std::string& handle_str, const userid_t user_id, const void* application_id, const size_t application_id_size, uint32_t auth_token_len) {
+ std::string disk_decryption_secret_key = "";
+
+ std::string keystore_alias_subid;
+ // Can be stored in user 0, so check for both.
+ if (!Find_Keystore_Alias_SubID_And_Prep_Files(user_id, keystore_alias_subid, handle_str) &&
+ !Find_Keystore_Alias_SubID_And_Prep_Files(0, keystore_alias_subid, handle_str))
+ {
+ printf("failed to scan keystore alias subid and prep keystore files\n");
+ return disk_decryption_secret_key;
+ }
+
+ // First get the keystore service
+ sp<IBinder> binder = getKeystoreBinderRetry();
+#ifdef USE_KEYSTORAGE_4
+ sp<IKeystoreService> service = interface_cast<IKeystoreService>(binder);
+#else
+ sp<IKeystoreService> service = interface_cast<IKeystoreService>(binder);
+#endif
+ if (service == NULL) {
+ printf("error: could not connect to keystore service\n");
+ return disk_decryption_secret_key;
+ }
+
+ if (auth_token_len > 0) {
+ printf("Starting keystore_auth service...\n");
+ property_set("ctl.start", "keystore_auth");
+ }
+
+ // Read the data from the .spblob file per: https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#869
+ std::string spblob_file = spblob_path + handle_str + ".spblob";
+ std::string spblob_data;
+ if (!android::base::ReadFileToString(spblob_file, &spblob_data)) {
+ printf("Failed to read '%s'\n", spblob_file.c_str());
+ return disk_decryption_secret_key;
+ }
+ unsigned char* byteptr = (unsigned char*)spblob_data.data();
+ if (*byteptr != SYNTHETIC_PASSWORD_VERSION_V2 && *byteptr != SYNTHETIC_PASSWORD_VERSION_V1
+ && *byteptr != SYNTHETIC_PASSWORD_VERSION_V3) {
+ printf("Unsupported synthetic password version %i\n", *byteptr);
+ return disk_decryption_secret_key;
+ }
+ const unsigned char* synthetic_password_version = byteptr;
+ byteptr++;
+ if (*byteptr != SYNTHETIC_PASSWORD_PASSWORD_BASED) {
+ printf("spblob data is not SYNTHETIC_PASSWORD_PASSWORD_BASED\n");
+ return disk_decryption_secret_key;
+ }
+ byteptr++; // Now we're pointing to the blob data itself
+ if (*synthetic_password_version == SYNTHETIC_PASSWORD_VERSION_V1) {
+ printf("spblob v1\n");
+ /* We're now going to handle decryptSPBlob: https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java#115
+ * Called from https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#879
+ * This small function ends up being quite a headache. The call to get data from the keystore basically is not needed in TWRP at this time.
+ * The keystore data seems to be the serialized data from an entire class in Java. Specifically I think it represents:
+ * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java
+ * or perhaps
+ * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/keystore/java/android/security/keystore/AndroidKeyStoreAuthenticatedAESCipherSpi.java
+ * but the only things we "need" from this keystore are a user ID and the keyAlias which ends up being USRSKEY_synthetic_password_{handle_str}
+ * the latter of which we already have. We may need to figure out how to get the user ID if we ever support decrypting mulitple users.
+ * There are 2 calls to a Java decrypt funcion that is overloaded. These 2 calls go in completely different directions despite the seemingly
+ * similar use of decrypt() and decrypt parameters. To figure out where things were going, I added logging to:
+ * https://android.googlesource.com/platform/libcore/+/android-8.0.0_r23/ojluni/src/main/java/javax/crypto/Cipher.java#2575
+ * Logger.global.severe("Cipher tryCombinations " + prov.getName() + " - " + prov.getInfo());
+ * To make logging work in libcore, import java.util.logging.Logger; and either set a better logging level or modify the framework to log everything
+ * regardless of logging level. This will give you some strings that you can grep for and find the actual crypto provider in use. In our case there were
+ * 2 different providers in use. The first stage to get the intermediate key used:
+ * https://android.googlesource.com/platform/external/conscrypt/+/android-8.0.0_r23/common/src/main/java/org/conscrypt/OpenSSLProvider.java
+ * which is a pretty straight-forward OpenSSL implementation of AES/GCM/NoPadding. */
+ // First we personalize as seen https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java#102
+ void* personalized_application_id = PersonalizedHashBinary(PERSONALISATION_APPLICATION_ID, (const char*)application_id, application_id_size);
+ if (!personalized_application_id) {
+ printf("malloc personalized_application_id\n");
+ return disk_decryption_secret_key;
+ }
+ //printf("personalized application id: "); output_hex((unsigned char*)personalized_application_id, SHA512_DIGEST_LENGTH); printf("\n");
+ // Now we'll decrypt using openssl AES/GCM/NoPadding
+ OpenSSL_add_all_ciphers();
+ int actual_size=0, final_size=0;
+ EVP_CIPHER_CTX *d_ctx = EVP_CIPHER_CTX_new();
+ const unsigned char* iv = (const unsigned char*)byteptr; // The IV is the first 12 bytes of the spblob
+ //printf("iv: "); output_hex((const unsigned char*)iv, 12); printf("\n");
+ const unsigned char* cipher_text = (const unsigned char*)byteptr + 12; // The cipher text comes immediately after the IV
+ //printf("cipher_text: "); output_hex((const unsigned char*)cipher_text, spblob_data.size() - 2 - 12); printf("\n");
+ const unsigned char* key = (const unsigned char*)personalized_application_id; // The key is the now personalized copy of the application ID
+ //printf("key: "); output_hex((const unsigned char*)key, 32); printf("\n");
+ EVP_DecryptInit(d_ctx, EVP_aes_256_gcm(), key, iv);
+ std::vector<unsigned char> intermediate_key;
+ intermediate_key.resize(spblob_data.size() - 2 - 12, '\0');
+ EVP_DecryptUpdate(d_ctx, &intermediate_key[0], &actual_size, cipher_text, spblob_data.size() - 2 - 12);
+ unsigned char tag[AES_BLOCK_SIZE];
+ EVP_CIPHER_CTX_ctrl(d_ctx, EVP_CTRL_GCM_SET_TAG, 16, tag);
+ EVP_DecryptFinal_ex(d_ctx, &intermediate_key[actual_size], &final_size);
+ EVP_CIPHER_CTX_free(d_ctx);
+ free(personalized_application_id);
+ //printf("spblob_data size: %lu actual_size %i, final_size: %i\n", spblob_data.size(), actual_size, final_size);
+ intermediate_key.resize(actual_size + final_size - 16, '\0');// not sure why we have to trim the size by 16 as I don't see where this is done in Java side
+ //printf("intermediate key: "); output_hex((const unsigned char*)intermediate_key.data(), intermediate_key.size()); printf("\n");
+
+ // When using secdis (aka not weaver) you must supply an auth token to the keystore prior to the begin operation
+ if (auth_token_len > 0) {
+ /*::keystore::KeyStoreServiceReturnCode auth_result = service->addAuthToken(auth_token, auth_token_len);
+ if (!auth_result.isOk()) {
+ // The keystore checks the uid of the calling process and will return a permission denied on this operation for user 0
+ printf("keystore error adding auth token\n");
+ return disk_decryption_secret_key;
+ }*/
+ // The keystore refuses to allow the root user to supply auth tokens, so we write the auth token to a file earlier and
+ // run a separate service that runs user the system user to add the auth token. We wait for the auth token file to be
+ // deleted by the keymaster_auth service and check for a /auth_error file in case of errors. We quit after after a while if
+ // the /auth_token file never gets deleted.
+ int auth_wait_count = 20;
+ while (access("/auth_token", F_OK) == 0 && auth_wait_count-- > 0)
+ usleep(5000);
+ if (auth_wait_count == 0 || access("/auth_error", F_OK) == 0) {
+ printf("error during keymaster_auth service\n");
+ /* If you are getting this error, make sure that you have the keymaster_auth service defined in your init scripts, preferrably in init.recovery.{ro.hardware}.rc
+ * service keystore_auth /system/bin/keystore_auth
+ * disabled
+ * oneshot
+ * user system
+ * group root
+ * seclabel u:r:recovery:s0
+ *
+ * And check dmesg for error codes regarding this service if needed. */
+ return disk_decryption_secret_key;
+ }
+ }
+
+ int32_t ret;
+
+ /* We only need a keyAlias which is USRSKEY_synthetic_password_b6f71045af7bd042 which we find and a uid which is -1 or 1000, I forget which
+ * as the key data will be read again by the begin function later via the keystore.
+ * The data is in a hidl_vec format which consists of a type and a value. */
+ /*::keystore::hidl_vec<uint8_t> data;
+ std::string keystoreid = SYNTHETIC_PASSWORD_KEY_PREFIX;
+ keystoreid += handle_str;
+
+ ret = service->get(String16(keystoreid.c_str()), user_id, &data);
+ if (ret < 0) {
+ printf("Could not connect to keystore service %i\n", ret);
+ return disk_decryption_secret_key;
+ } else if (ret != 1 /*android::keystore::ResponseCode::NO_ERROR*//*) {
+ printf("keystore error: (%d)\n", /*responses[ret],*//* ret);
+ return disk_decryption_secret_key;
+ } else {
+ printf("keystore returned: "); output_hex(&data[0], data.size()); printf("\n");
+ }*/
+
+ // Now we'll break up the intermediate key into the IV (first 12 bytes) and the cipher text (the rest of it).
+ std::vector<unsigned char> nonce = intermediate_key;
+ nonce.resize(12);
+ intermediate_key.erase (intermediate_key.begin(),intermediate_key.begin()+12);
+ //printf("nonce: "); output_hex((const unsigned char*)nonce.data(), nonce.size()); printf("\n");
+ //printf("cipher text: "); output_hex((const unsigned char*)intermediate_key.data(), intermediate_key.size()); printf("\n");
+
+ /* Now we will begin the second decrypt call found in
+ * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java#122
+ * This time we will use https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java
+ * and https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/keystore/java/android/security/keystore/AndroidKeyStoreAuthenticatedAESCipherSpi.java
+ * First we set some algorithm parameters as seen in two places:
+ * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/keystore/java/android/security/keystore/AndroidKeyStoreAuthenticatedAESCipherSpi.java#297
+ * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/keystore/java/android/security/keystore/AndroidKeyStoreAuthenticatedAESCipherSpi.java#216 */
+ size_t maclen = 128;
+ ::keystore::AuthorizationSetBuilder begin_params;
+ begin_params.Authorization(::keystore::TAG_ALGORITHM, ::keystore::Algorithm::AES);
+ begin_params.Authorization(::keystore::TAG_BLOCK_MODE, ::keystore::BlockMode::GCM);
+ begin_params.Padding(::keystore::PaddingMode::NONE);
+ begin_params.Authorization(::keystore::TAG_NONCE, nonce);
+ begin_params.Authorization(::keystore::TAG_MAC_LENGTH, maclen);
+ //keymasterArgs.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_AES);
+ //keymasterArgs.addEnum(KeymasterDefs.KM_TAG_BLOCK_MODE, mKeymasterBlockMode);
+ //keymasterArgs.addEnum(KeymasterDefs.KM_TAG_PADDING, mKeymasterPadding);
+ //keymasterArgs.addUnsignedInt(KeymasterDefs.KM_TAG_MAC_LENGTH, mTagLengthBits);
+ ::keystore::hidl_vec<uint8_t> entropy; // No entropy is needed for decrypt
+ entropy.resize(0);
+ std::string keystore_alias = mKey_Prefix;
+ keystore_alias += keystore_alias_subid;
+ String16 keystore_alias16(keystore_alias.c_str());
+#ifdef USE_KEYSTORAGE_4
+ android::hardware::keymaster::V4_0::KeyPurpose purpose = android::hardware::keymaster::V4_0::KeyPurpose::DECRYPT;
+ security::keymaster::OperationResult begin_result;
+ security::keymaster::OperationResult update_result;
+ security::keymaster::OperationResult finish_result;
+ ::android::security::keymaster::KeymasterArguments empty_params;
+ // These parameters are mostly driven by the cipher.init call https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java#63
+ service->begin(binder, keystore_alias16, (int32_t)purpose, true, android::security::keymaster::KeymasterArguments(begin_params.hidl_data()), entropy, -1, &begin_result);
+#else
+ ::keystore::KeyPurpose purpose = ::keystore::KeyPurpose::DECRYPT;
+ OperationResult begin_result;
+ OperationResult update_result;
+ OperationResult finish_result;
+ ::keystore::hidl_vec<::keystore::KeyParameter> empty_params;
+ empty_params.resize(0);
+ // These parameters are mostly driven by the cipher.init call https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java#63
+ service->begin(binder, keystore_alias16, purpose, true, begin_params.hidl_data(), entropy, -1, &begin_result);
+#endif
+ ret = begin_result.resultCode;
+ if (ret != 1 /*android::keystore::ResponseCode::NO_ERROR*/) {
+ printf("keystore begin error: (%d)\n", /*responses[ret],*/ ret);
+ return disk_decryption_secret_key;
+ } else {
+ //printf("keystore begin operation successful\n");
+ }
+ // The cipher.doFinal call triggers an update to the keystore followed by a finish https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java#64
+ // See also https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java#208
+ service->update(begin_result.token, empty_params, intermediate_key, &update_result);
+ ret = update_result.resultCode;
+ if (ret != 1 /*android::keystore::ResponseCode::NO_ERROR*/) {
+ printf("keystore update error: (%d)\n", /*responses[ret],*/ ret);
+ return disk_decryption_secret_key;
+ } else {
+ //printf("keystore update operation successful\n");
+ //printf("keystore update returned: "); output_hex(&update_result.data[0], update_result.data.size()); printf("\n"); // this ends up being the synthetic password
+ }
+ // We must use the data in update_data.data before we call finish below or the data will be gone
+ // The payload data from the keystore update is further personalized at https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#153
+ // We now have the disk decryption key!
+ disk_decryption_secret_key = PersonalizedHash(PERSONALIZATION_FBE_KEY, (const char*)&update_result.data[0], update_result.data.size());
+ //printf("disk_decryption_secret_key: '%s'\n", disk_decryption_secret_key.c_str());
+ ::keystore::hidl_vec<uint8_t> signature;
+ service->finish(begin_result.token, empty_params, signature, entropy, &finish_result);
+ ret = finish_result.resultCode;
+ if (ret != 1 /*android::keystore::ResponseCode::NO_ERROR*/) {
+ printf("keystore finish error: (%d)\n", /*responses[ret],*/ ret);
+ return disk_decryption_secret_key;
+ } else {
+ //printf("keystore finish operation successful\n");
+ }
+ stop_keystore();
+ return disk_decryption_secret_key;
+ } else if (*synthetic_password_version == SYNTHETIC_PASSWORD_VERSION_V2
+ || *synthetic_password_version == SYNTHETIC_PASSWORD_VERSION_V3) {
+ printf("spblob v2 / v3\n");
+ /* Version 2 / 3 of the spblob is basically the same as version 1, but the order of getting the intermediate key and disk decryption key have been flip-flopped
+ * as seen in https://android.googlesource.com/platform/frameworks/base/+/5025791ac6d1538224e19189397de8d71dcb1a12
+ */
+ /* First decrypt call found in
+ * https://android.googlesource.com/platform/frameworks/base/+/android-8.1.0_r18/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java#135
+ * We will use https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java
+ * and https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/keystore/java/android/security/keystore/AndroidKeyStoreAuthenticatedAESCipherSpi.java
+ * First we set some algorithm parameters as seen in two places:
+ * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/keystore/java/android/security/keystore/AndroidKeyStoreAuthenticatedAESCipherSpi.java#297
+ * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/keystore/java/android/security/keystore/AndroidKeyStoreAuthenticatedAESCipherSpi.java#216 */
+ // When using secdis (aka not weaver) you must supply an auth token to the keystore prior to the begin operation
+ if (auth_token_len > 0) {
+ /*::keystore::KeyStoreServiceReturnCode auth_result = service->addAuthToken(auth_token, auth_token_len);
+ if (!auth_result.isOk()) {
+ // The keystore checks the uid of the calling process and will return a permission denied on this operation for user 0
+ printf("keystore error adding auth token\n");
+ return disk_decryption_secret_key;
+ }*/
+ // The keystore refuses to allow the root user to supply auth tokens, so we write the auth token to a file earlier and
+ // run a separate service that runs user the system user to add the auth token. We wait for the auth token file to be
+ // deleted by the keymaster_auth service and check for a /auth_error file in case of errors. We quit after after a while if
+ // the /auth_token file never gets deleted.
+ int auth_wait_count = 20;
+ while (access("/auth_token", F_OK) == 0 && auth_wait_count-- > 0)
+ usleep(5000);
+ if (auth_wait_count == 0 || access("/auth_error", F_OK) == 0) {
+ printf("error during keymaster_auth service\n");
+ /* If you are getting this error, make sure that you have the keymaster_auth service defined in your init scripts, preferrably in init.recovery.{ro.hardware}.rc
+ * service keystore_auth /system/bin/keystore_auth
+ * disabled
+ * oneshot
+ * user system
+ * group root
+ * seclabel u:r:recovery:s0
+ *
+ * And check dmesg for error codes regarding this service if needed. */
+ return disk_decryption_secret_key;
+ }
+ }
+ int32_t ret;
+ size_t maclen = 128;
+ unsigned char* iv = (unsigned char*)byteptr; // The IV is the first 12 bytes of the spblob
+ ::keystore::hidl_vec<uint8_t> iv_hidlvec;
+ iv_hidlvec.setToExternal((unsigned char*)byteptr, 12);
+ //printf("iv: "); output_hex((const unsigned char*)iv, 12); printf("\n");
+ unsigned char* cipher_text = (unsigned char*)byteptr + 12; // The cipher text comes immediately after the IV
+ ::keystore::hidl_vec<uint8_t> cipher_text_hidlvec;
+ cipher_text_hidlvec.setToExternal(cipher_text, spblob_data.size() - 14 /* 1 each for version and SYNTHETIC_PASSWORD_PASSWORD_BASED and 12 for the iv */);
+ ::keystore::AuthorizationSetBuilder begin_params;
+ begin_params.Authorization(::keystore::TAG_ALGORITHM, ::keystore::Algorithm::AES);
+ begin_params.Authorization(::keystore::TAG_BLOCK_MODE, ::keystore::BlockMode::GCM);
+ begin_params.Padding(::keystore::PaddingMode::NONE);
+ begin_params.Authorization(::keystore::TAG_NONCE, iv_hidlvec);
+ begin_params.Authorization(::keystore::TAG_MAC_LENGTH, maclen);
+ ::keystore::hidl_vec<uint8_t> entropy; // No entropy is needed for decrypt
+ entropy.resize(0);
+ std::string keystore_alias = mKey_Prefix;
+ keystore_alias += keystore_alias_subid;
+ String16 keystore_alias16(keystore_alias.c_str());
+#ifdef USE_KEYSTORAGE_4
+ android::hardware::keymaster::V4_0::KeyPurpose purpose = android::hardware::keymaster::V4_0::KeyPurpose::DECRYPT;
+ security::keymaster::OperationResult begin_result;
+ security::keymaster::OperationResult update_result;
+ security::keymaster::OperationResult finish_result;
+ ::android::security::keymaster::KeymasterArguments empty_params;
+ // These parameters are mostly driven by the cipher.init call https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java#63
+ service->begin(binder, keystore_alias16, (int32_t)purpose, true, android::security::keymaster::KeymasterArguments(begin_params.hidl_data()), entropy, -1, &begin_result);
+#else
+ ::keystore::KeyPurpose purpose = ::keystore::KeyPurpose::DECRYPT;
+ OperationResult begin_result;
+ OperationResult update_result;
+ OperationResult finish_result;
+ ::keystore::hidl_vec<::keystore::KeyParameter> empty_params;
+ empty_params.resize(0);
+ // These parameters are mostly driven by the cipher.init call https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java#63
+ service->begin(binder, keystore_alias16, purpose, true, begin_params.hidl_data(), entropy, -1, &begin_result);
+#endif
+ ret = begin_result.resultCode;
+ if (ret != 1 /*android::keystore::ResponseCode::NO_ERROR*/) {
+ printf("keystore begin error: (%d)\n", /*responses[ret],*/ ret);
+ return disk_decryption_secret_key;
+ } /*else {
+ printf("keystore begin operation successful\n");
+ }*/
+ // The cipher.doFinal call triggers an update to the keystore followed by a finish https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java#64
+ // See also https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java#208
+ service->update(begin_result.token, empty_params, cipher_text_hidlvec, &update_result);
+ ret = update_result.resultCode;
+ if (ret != 1 /*android::keystore::ResponseCode::NO_ERROR*/) {
+ printf("keystore update error: (%d)\n", /*responses[ret],*/ ret);
+ return disk_decryption_secret_key;
+ } /*else {
+ printf("keystore update operation successful\n");
+ printf("keystore update returned: "); output_hex(&update_result.data[0], update_result.data.size()); printf("\n"); // this ends up being the synthetic password
+ }*/
+ //printf("keystore resulting data: "); output_hex((unsigned char*)&update_result.data[0], update_result.data.size()); printf("\n");
+ // We must copy the data in update_data.data before we call finish below or the data will be gone
+ size_t keystore_result_size = update_result.data.size();
+ unsigned char* keystore_result = (unsigned char*)malloc(keystore_result_size);
+ if (!keystore_result) {
+ printf("malloc on keystore_result\n");
+ return disk_decryption_secret_key;
+ }
+ memcpy(keystore_result, &update_result.data[0], update_result.data.size());
+ //printf("keystore_result data: "); output_hex(keystore_result, keystore_result_size); printf("\n");
+ ::keystore::hidl_vec<uint8_t> signature;
+ service->finish(begin_result.token, empty_params, signature, entropy, &finish_result);
+ ret = finish_result.resultCode;
+ if (ret != 1 /*android::keystore::ResponseCode::NO_ERROR*/) {
+ printf("keystore finish error: (%d)\n", /*responses[ret],*/ ret);
+ free(keystore_result);
+ return disk_decryption_secret_key;
+ } /*else {
+ printf("keystore finish operation successful\n");
+ }*/
+ stop_keystore();
+
+ /* Now we do the second decrypt call as seen in:
+ * https://android.googlesource.com/platform/frameworks/base/+/android-8.1.0_r18/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java#136
+ */
+ const unsigned char* intermediate_iv = keystore_result;
+ //printf("intermediate_iv: "); output_hex((const unsigned char*)intermediate_iv, 12); printf("\n");
+ const unsigned char* intermediate_cipher_text = (const unsigned char*)keystore_result + 12; // The cipher text comes immediately after the IV
+ int cipher_size = keystore_result_size - 12;
+ //printf("intermediate_cipher_text: "); output_hex((const unsigned char*)intermediate_cipher_text, cipher_size); printf("\n");
+ // First we personalize as seen https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java#102
+ void* personalized_application_id = PersonalizedHashBinary(PERSONALISATION_APPLICATION_ID, (const char*)application_id, application_id_size);
+ if (!personalized_application_id) {
+ printf("malloc personalized_application_id\n");
+ free(keystore_result);
+ return disk_decryption_secret_key;
+ }
+ //printf("personalized application id: "); output_hex((unsigned char*)personalized_application_id, SHA512_DIGEST_LENGTH); printf("\n");
+ // Now we'll decrypt using openssl AES/GCM/NoPadding
+ OpenSSL_add_all_ciphers();
+ int actual_size=0, final_size=0;
+ EVP_CIPHER_CTX *d_ctx = EVP_CIPHER_CTX_new();
+ const unsigned char* key = (const unsigned char*)personalized_application_id; // The key is the now personalized copy of the application ID
+ //printf("key: "); output_hex((const unsigned char*)key, 32); printf("\n");
+ EVP_DecryptInit(d_ctx, EVP_aes_256_gcm(), key, intermediate_iv);
+ unsigned char* secret_key = (unsigned char*)malloc(cipher_size);
+ EVP_DecryptUpdate(d_ctx, secret_key, &actual_size, intermediate_cipher_text, cipher_size);
+ unsigned char tag[AES_BLOCK_SIZE];
+ EVP_CIPHER_CTX_ctrl(d_ctx, EVP_CTRL_GCM_SET_TAG, 16, tag);
+ EVP_DecryptFinal_ex(d_ctx, secret_key + actual_size, &final_size);
+ EVP_CIPHER_CTX_free(d_ctx);
+ free(personalized_application_id);
+ free(keystore_result);
+ int secret_key_real_size = actual_size - 16;
+ //printf("secret key: "); output_hex((const unsigned char*)secret_key, secret_key_real_size); printf("\n");
+ // The payload data from the keystore update is further personalized at https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#153
+ // We now have the disk decryption key!
+ if (*synthetic_password_version == SYNTHETIC_PASSWORD_VERSION_V3) {
+ // V3 uses SP800 instead of SHA512
+ disk_decryption_secret_key = PersonalizedHashSP800(PERSONALIZATION_FBE_KEY, PERSONALISATION_CONTEXT, (const char*)secret_key, secret_key_real_size);
+ } else {
+ disk_decryption_secret_key = PersonalizedHash(PERSONALIZATION_FBE_KEY, (const char*)secret_key, secret_key_real_size);
+ }
+ //printf("disk_decryption_secret_key: '%s'\n", disk_decryption_secret_key.c_str());
+ free(secret_key);
+ return disk_decryption_secret_key;
+ }
+ return disk_decryption_secret_key;
+}
+
+}}
+
+#define PASSWORD_TOKEN_SIZE 32
+
+/* C++ replacement for
+ * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#992
+ * called here
+ * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#813 */
+bool Get_Secdis(const std::string& spblob_path, const std::string& handle_str, std::string& secdis_data) {
+ std::string secdis_file = spblob_path + handle_str + ".secdis";
+ if (!android::base::ReadFileToString(secdis_file, &secdis_data)) {
+ printf("Failed to read '%s'\n", secdis_file.c_str());
+ return false;
+ }
+ //output_hex(secdis_data.data(), secdis_data.size());printf("\n");
+ return true;
+}
+
+// C++ replacement for https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#1033
+userid_t fakeUid(const userid_t uid) {
+ return 100000 + uid;
+}
+
+bool Is_Weaver(const std::string& spblob_path, const std::string& handle_str) {
+ std::string weaver_file = spblob_path + handle_str + ".weaver";
+ struct stat st;
+ if (stat(weaver_file.c_str(), &st) == 0)
+ return true;
+ return false;
+}
+
+bool Free_Return(bool retval, void* weaver_key, password_data_struct* pwd) {
+ if (weaver_key)
+ free(weaver_key);
+ if (pwd->salt)
+ free(pwd->salt);
+ if (pwd->password_handle)
+ free(pwd->password_handle);
+ return retval;
+}
+
+/* Decrypt_User_Synth_Pass is the TWRP C++ equivalent to spBasedDoVerifyCredential
+ * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/LockSettingsService.java#1998 */
+bool Decrypt_User_Synth_Pass(const userid_t user_id, const std::string& Password) {
+ bool retval = false;
+ void* weaver_key = NULL;
+ password_data_struct pwd;
+ pwd.salt = NULL;
+ pwd.salt_len = 0;
+ pwd.password_handle = NULL;
+ pwd.handle_len = 0;
+ char application_id[PASSWORD_TOKEN_SIZE + SHA512_DIGEST_LENGTH];
+
+ uint32_t auth_token_len = 0;
+
+ std::string secret; // this will be the disk decryption key that is sent to vold
+ std::string token = "!"; // there is no token used for this kind of decrypt, key escrow is handled by weaver
+ int flags = FLAG_STORAGE_DE;
+ if (user_id == 0)
+ flags = FLAG_STORAGE_DE;
+ else
+ flags = FLAG_STORAGE_CE;
+ char spblob_path_char[PATH_MAX];
+ sprintf(spblob_path_char, "/data/system_de/%d/spblob/", user_id);
+ std::string spblob_path = spblob_path_char;
+ long handle = 0;
+ std::string handle_str;
+ // Get the handle: https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/LockSettingsService.java#2017
+ if (!Find_Handle(spblob_path, handle_str)) {
+ printf("Error getting handle\n");
+ return Free_Return(retval, weaver_key, &pwd);
+ }
+ printf("Handle is '%s'\n", handle_str.c_str());
+ // Now we begin driving unwrapPasswordBasedSyntheticPassword from: https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#758
+ // First we read the password data which contains scrypt parameters
+ if (!Get_Password_Data(spblob_path, handle_str, &pwd)) {
+ printf("Failed to Get_Password_Data\n");
+ return Free_Return(retval, weaver_key, &pwd);
+ }
+ //printf("pwd N %i R %i P %i salt ", pwd.scryptN, pwd.scryptR, pwd.scryptP); output_hex((char*)pwd.salt, pwd.salt_len); printf("\n");
+ unsigned char password_token[PASSWORD_TOKEN_SIZE];
+ //printf("Password: '%s'\n", Password.c_str());
+ // The password token is the password scrypted with the parameters from the password data file
+ if (!Get_Password_Token(&pwd, Password, &password_token[0])) {
+ printf("Failed to Get_Password_Token\n");
+ return Free_Return(retval, weaver_key, &pwd);
+ }
+ //output_hex(&password_token[0], PASSWORD_TOKEN_SIZE);printf("\n");
+ if (Is_Weaver(spblob_path, handle_str)) {
+ printf("using weaver\n");
+ // BEGIN PIXEL 2 WEAVER
+ // Get the weaver data from the .weaver file which tells us which slot to use when we ask weaver for the escrowed key
+ // https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#768
+ weaver_data_struct wd;
+ if (!Get_Weaver_Data(spblob_path, handle_str, &wd)) {
+ printf("Failed to get weaver data\n");
+ return Free_Return(retval, weaver_key, &pwd);
+ }
+ // The weaver key is the the password token prefixed with "weaver-key" padded to 128 with nulls with the password token appended then SHA512
+ // https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#1059
+ weaver_key = PersonalizedHashBinary(PERSONALISATION_WEAVER_KEY, (char*)&password_token[0], PASSWORD_TOKEN_SIZE);
+ if (!weaver_key) {
+ printf("malloc error getting weaver_key\n");
+ return Free_Return(retval, weaver_key, &pwd);
+ }
+ // Now we start driving weaverVerify: https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#343
+ // Called from https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#776
+ android::vold::Weaver weaver;
+ if (!weaver) {
+ printf("Failed to get weaver service\n");
+ return Free_Return(retval, weaver_key, &pwd);
+ }
+ // Get the key size from weaver service
+ uint32_t weaver_key_size = 0;
+ if (!weaver.GetKeySize(&weaver_key_size)) {
+ printf("Failed to get weaver key size\n");
+ return Free_Return(retval, weaver_key, &pwd);
+ } else {
+ //printf("weaver key size is %u\n", weaver_key_size);
+ }
+ //printf("weaver key: "); output_hex((unsigned char*)weaver_key, weaver_key_size); printf("\n");
+ // Send the slot from the .weaver file, the computed weaver key, and get the escrowed key data
+ std::vector<uint8_t> weaver_payload;
+ // TODO: we should return more information about the status including time delays before the next retry
+ if (!weaver.WeaverVerify(wd.slot, weaver_key, &weaver_payload)) {
+ printf("failed to weaver verify\n");
+ return Free_Return(retval, weaver_key, &pwd);
+ }
+ //printf("weaver payload: "); output_hex(&weaver_payload); printf("\n");
+ // Done with weaverVerify
+ // Now we will compute the application ID
+ // https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#964
+ // Called from https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#780
+ // The escrowed weaver key data is prefixed with "weaver-pwd" padded to 128 with nulls with the weaver payload appended then SHA512
+ void* weaver_secret = PersonalizedHashBinary(PERSONALISATION_WEAVER_PASSWORD, (const char*)weaver_payload.data(), weaver_payload.size());
+ //printf("weaver secret: "); output_hex((unsigned char*)weaver_secret, SHA512_DIGEST_LENGTH); printf("\n");
+ // The application ID is the password token and weaver secret appended to each other
+ memcpy((void*)&application_id[0], (void*)&password_token[0], PASSWORD_TOKEN_SIZE);
+ memcpy((void*)&application_id[PASSWORD_TOKEN_SIZE], weaver_secret, SHA512_DIGEST_LENGTH);
+ //printf("application ID: "); output_hex((unsigned char*)application_id, PASSWORD_TOKEN_SIZE + SHA512_DIGEST_LENGTH); printf("\n");
+ // END PIXEL 2 WEAVER
+ } else {
+ printf("using secdis\n");
+ std::string secdis_data;
+ if (!Get_Secdis(spblob_path, handle_str, secdis_data)) {
+ printf("Failed to get secdis data\n");
+ return Free_Return(retval, weaver_key, &pwd);
+ }
+ void* secdiscardable = PersonalizedHashBinary(PERSONALISATION_SECDISCARDABLE, (char*)secdis_data.data(), secdis_data.size());
+ if (!secdiscardable) {
+ printf("malloc error getting secdiscardable\n");
+ return Free_Return(retval, weaver_key, &pwd);
+ }
+ memcpy((void*)&application_id[0], (void*)&password_token[0], PASSWORD_TOKEN_SIZE);
+ memcpy((void*)&application_id[PASSWORD_TOKEN_SIZE], secdiscardable, SHA512_DIGEST_LENGTH);
+
+ int ret = -1;
+ bool request_reenroll = false;
+ android::sp<android::hardware::gatekeeper::V1_0::IGatekeeper> gk_device;
+ gk_device = ::android::hardware::gatekeeper::V1_0::IGatekeeper::getService();
+ if (gk_device == nullptr) {
+ printf("failed to get gatekeeper service\n");
+ return Free_Return(retval, weaver_key, &pwd);
+ }
+ if (pwd.handle_len <= 0) {
+ printf("no password handle supplied\n");
+ return Free_Return(retval, weaver_key, &pwd);
+ }
+ android::hardware::hidl_vec<uint8_t> pwd_handle_hidl;
+ pwd_handle_hidl.setToExternal(const_cast<uint8_t *>((const uint8_t *)pwd.password_handle), pwd.handle_len);
+ void* gk_pwd_token = PersonalizedHashBinary(PERSONALIZATION_USER_GK_AUTH, (char*)&password_token[0], PASSWORD_TOKEN_SIZE);
+ if (!gk_pwd_token) {
+ printf("malloc error getting gatekeeper_key\n");
+ return Free_Return(retval, weaver_key, &pwd);
+ }
+ android::hardware::hidl_vec<uint8_t> gk_pwd_token_hidl;
+ gk_pwd_token_hidl.setToExternal(const_cast<uint8_t *>((const uint8_t *)gk_pwd_token), SHA512_DIGEST_LENGTH);
+ android::hardware::Return<void> hwRet =
+ gk_device->verify(fakeUid(user_id), 0 /* challange */,
+ pwd_handle_hidl,
+ gk_pwd_token_hidl,
+ [&ret, &request_reenroll, &auth_token_len]
+ (const android::hardware::gatekeeper::V1_0::GatekeeperResponse &rsp) {
+ ret = static_cast<int>(rsp.code); // propagate errors
+ if (rsp.code >= android::hardware::gatekeeper::V1_0::GatekeeperStatusCode::STATUS_OK) {
+ auth_token_len = rsp.data.size();
+ request_reenroll = (rsp.code == android::hardware::gatekeeper::V1_0::GatekeeperStatusCode::STATUS_REENROLL);
+ ret = 0; // all success states are reported as 0
+ // The keystore refuses to allow the root user to supply auth tokens, so we write the auth token to a file here and later
+ // run a separate service that runs as the system user to add the auth token. We wait for the auth token file to be
+ // deleted by the keymaster_auth service and check for a /auth_error file in case of errors. We quit after a while seconds if
+ // the /auth_token file never gets deleted.
+ unlink("/auth_token");
+ FILE* auth_file = fopen("/auth_token","wb");
+ if (auth_file != NULL) {
+ fwrite(rsp.data.data(), sizeof(uint8_t), rsp.data.size(), auth_file);
+ fclose(auth_file);
+ } else {
+ printf("failed to open /auth_token for writing\n");
+ ret = -2;
+ }
+ } else if (rsp.code == android::hardware::gatekeeper::V1_0::GatekeeperStatusCode::ERROR_RETRY_TIMEOUT && rsp.timeout > 0) {
+ ret = rsp.timeout;
+ }
+ }
+ );
+ free(gk_pwd_token);
+ if (!hwRet.isOk() || ret != 0) {
+ printf("gatekeeper verification failed\n");
+ return Free_Return(retval, weaver_key, &pwd);
+ }
+ }
+ // Now we will handle https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#816
+ // Plus we will include the last bit that computes the disk decrypt key found in:
+ // https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#153
+ secret = android::keystore::unwrapSyntheticPasswordBlob(spblob_path, handle_str, user_id, (const void*)&application_id[0], PASSWORD_TOKEN_SIZE + SHA512_DIGEST_LENGTH, auth_token_len);
+ if (!secret.size()) {
+ printf("failed to unwrapSyntheticPasswordBlob\n");
+ return Free_Return(retval, weaver_key, &pwd);
+ }
+ if (!e4crypt_unlock_user_key(user_id, 0, token.c_str(), secret.c_str())) {
+ printf("e4crypt_unlock_user_key returned fail\n");
+ return Free_Return(retval, weaver_key, &pwd);
+ }
+/*#ifdef USE_KEYSTORAGE_4
+ if (!e4crypt_prepare_user_storage("", user_id, 0, flags)) {
+#else
+ if (!e4crypt_prepare_user_storage(nullptr, user_id, 0, flags)) {
+#endif
+ printf("failed to e4crypt_prepare_user_storage\n");
+ return Free_Return(retval, weaver_key, &pwd);
+ }*/
+ printf("User %i Decrypted Successfully!\n", user_id);
+ retval = true;
+ return Free_Return(retval, weaver_key, &pwd);
+}
+#endif //HAVE_SYNTH_PWD_SUPPORT
+
+int Get_Password_Type(const userid_t user_id, std::string& filename) {
+ struct stat st;
+ char spblob_path_char[PATH_MAX];
+ sprintf(spblob_path_char, "/data/system_de/%d/spblob/", user_id);
+ if (stat(spblob_path_char, &st) == 0) {
+#ifdef HAVE_SYNTH_PWD_SUPPORT
+ printf("Using synthetic password method\n");
+ std::string spblob_path = spblob_path_char;
+ std::string handle_str;
+ if (!Find_Handle(spblob_path, handle_str)) {
+ printf("Error getting handle\n");
+ return 0;
+ }
+ printf("Handle is '%s'\n", handle_str.c_str());
+ password_data_struct pwd;
+ if (!Get_Password_Data(spblob_path, handle_str, &pwd)) {
+ printf("Failed to Get_Password_Data\n");
+ return 0;
+ }
+ if (pwd.password_type == 1) // In Android this means pattern
+ return 2; // In TWRP this means pattern
+ else if (pwd.password_type == 2) // In Android this means PIN or password
+ return 1; // In TWRP this means PIN or password
+ return 0; // We'll try the default password
+#else
+ printf("Synthetic password support not present in TWRP\n");
+ return -1;
+#endif
+ }
+ std::string path;
+ if (user_id == 0) {
+ path = "/data/system/";
+ } else {
+ char user_id_str[5];
+ sprintf(user_id_str, "%i", user_id);
+ path = "/data/system/users/";
+ path += user_id_str;
+ path += "/";
+ }
+ filename = path + "gatekeeper.password.key";
+ if (stat(filename.c_str(), &st) == 0 && st.st_size > 0)
+ return 1;
+ filename = path + "gatekeeper.pattern.key";
+ if (stat(filename.c_str(), &st) == 0 && st.st_size > 0)
+ return 2;
+ printf("Unable to locate gatekeeper password file '%s'\n", filename.c_str());
+ filename = "";
+ return 0;
+}
+
+bool Decrypt_User(const userid_t user_id, const std::string& Password) {
+ uint8_t *auth_token;
+ uint32_t auth_token_len;
+ int ret;
+
+ struct stat st;
+ if (user_id > 9999) {
+ printf("user_id is too big\n");
+ return false;
+ }
+ std::string filename;
+ bool Default_Password = (Password == "!");
+ if (Get_Password_Type(user_id, filename) == 0 && !Default_Password) {
+ printf("Unknown password type\n");
+ return false;
+ }
+ int flags = FLAG_STORAGE_DE;
+ if (user_id == 0)
+ flags = FLAG_STORAGE_DE;
+ else
+ flags = FLAG_STORAGE_CE;
+ if (Default_Password) {
+ if (!e4crypt_unlock_user_key(user_id, 0, "!", "!")) {
+ printf("e4crypt_unlock_user_key returned fail\n");
+ return false;
+ }
+/*#ifdef USE_KEYSTORAGE_4
+ if (!e4crypt_prepare_user_storage("", user_id, 0, flags)) {
+#else
+ if (!e4crypt_prepare_user_storage(nullptr, user_id, 0, flags)) {
+#endif
+ printf("failed to e4crypt_prepare_user_storage\n");
+ return false;
+ }*/
+ printf("User %i Decrypted Successfully!\n", user_id);
+ return true;
+ }
+ if (stat("/data/system_de/0/spblob", &st) == 0) {
+#ifdef HAVE_SYNTH_PWD_SUPPORT
+ printf("Using synthetic password method\n");
+ return Decrypt_User_Synth_Pass(user_id, Password);
+#else
+ printf("Synthetic password support not present in TWRP\n");
+ return false;
+#endif
+ }
+ printf("password filename is '%s'\n", filename.c_str());
+ if (stat(filename.c_str(), &st) != 0) {
+ printf("error stat'ing key file: %s\n", strerror(errno));
+ return false;
+ }
+ std::string handle;
+ if (!android::base::ReadFileToString(filename, &handle)) {
+ printf("Failed to read '%s'\n", filename.c_str());
+ return false;
+ }
+ bool should_reenroll;
+#ifdef HAVE_GATEKEEPER1
+ bool request_reenroll = false;
+ android::sp<android::hardware::gatekeeper::V1_0::IGatekeeper> gk_device;
+ gk_device = ::android::hardware::gatekeeper::V1_0::IGatekeeper::getService();
+ if (gk_device == nullptr)
+ return false;
+ android::hardware::hidl_vec<uint8_t> curPwdHandle;
+ curPwdHandle.setToExternal(const_cast<uint8_t *>((const uint8_t *)handle.c_str()), st.st_size);
+ android::hardware::hidl_vec<uint8_t> enteredPwd;
+ enteredPwd.setToExternal(const_cast<uint8_t *>((const uint8_t *)Password.c_str()), Password.size());
+
+ android::hardware::Return<void> hwRet =
+ gk_device->verify(user_id, 0 /* challange */,
+ curPwdHandle,
+ enteredPwd,
+ [&ret, &request_reenroll, &auth_token, &auth_token_len]
+ (const android::hardware::gatekeeper::V1_0::GatekeeperResponse &rsp) {
+ ret = static_cast<int>(rsp.code); // propagate errors
+ if (rsp.code >= android::hardware::gatekeeper::V1_0::GatekeeperStatusCode::STATUS_OK) {
+ auth_token = new uint8_t[rsp.data.size()];
+ auth_token_len = rsp.data.size();
+ memcpy(auth_token, rsp.data.data(), auth_token_len);
+ request_reenroll = (rsp.code == android::hardware::gatekeeper::V1_0::GatekeeperStatusCode::STATUS_REENROLL);
+ ret = 0; // all success states are reported as 0
+ } else if (rsp.code == android::hardware::gatekeeper::V1_0::GatekeeperStatusCode::ERROR_RETRY_TIMEOUT && rsp.timeout > 0) {
+ ret = rsp.timeout;
+ }
+ }
+ );
+ if (!hwRet.isOk()) {
+ return false;
+ }
+#else
+ gatekeeper_device_t *gk_device;
+ ret = gatekeeper_device_initialize(&gk_device);
+ if (ret!=0)
+ return false;
+ ret = gk_device->verify(gk_device, user_id, 0, (const uint8_t *)handle.c_str(), st.st_size,
+ (const uint8_t *)Password.c_str(), (uint32_t)Password.size(), &auth_token, &auth_token_len,
+ &should_reenroll);
+ if (ret !=0) {
+ printf("failed to verify\n");
+ return false;
+ }
+#endif
+ char token_hex[(auth_token_len*2)+1];
+ token_hex[(auth_token_len*2)] = 0;
+ uint32_t i;
+ for (i=0;i<auth_token_len;i++) {
+ sprintf(&token_hex[2*i], "%02X", auth_token[i]);
+ }
+ // The secret is "Android FBE credential hash" plus appended 0x00 to reach 128 bytes then append the user's password then feed that to sha512sum
+ std::string secret = HashPassword(Password);
+ if (!e4crypt_unlock_user_key(user_id, 0, token_hex, secret.c_str())) {
+ printf("e4crypt_unlock_user_key returned fail\n");
+ return false;
+ }
+/*#ifdef USE_KEYSTORAGE_4
+ if (!e4crypt_prepare_user_storage("", user_id, 0, flags)) {
+#else
+ if (!e4crypt_prepare_user_storage(nullptr, user_id, 0, flags)) {
+#endif
+ printf("failed to e4crypt_prepare_user_storage\n");
+ return false;
+ }*/
+ printf("User %i Decrypted Successfully!\n", user_id);
+ return true;
+}
diff --git a/crypto/ext4crypt/Decrypt.h b/crypto/ext4crypt/Decrypt.h
new file mode 100644
index 0000000..56318e2
--- /dev/null
+++ b/crypto/ext4crypt/Decrypt.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdbool.h>
+#include <sys/cdefs.h>
+
+#include <cutils/multiuser.h>
+
+#include <string>
+
+__BEGIN_DECLS
+
+// NOTE: keep in sync with StorageManager
+static constexpr int FLAG_STORAGE_DE = 1 << 0;
+static constexpr int FLAG_STORAGE_CE = 1 << 1;
+// For 9.0 Ext4CryptPie.cpp
+static constexpr int STORAGE_FLAG_DE = 1 << 0;
+static constexpr int STORAGE_FLAG_CE = 1 << 1;
+
+
+int Get_Password_Type(const userid_t user_id, std::string& filename);
+bool Decrypt_DE();
+bool Decrypt_User(const userid_t user_id, const std::string& Password);
+
+__END_DECLS
diff --git a/crypto/ext4crypt/Ext4Crypt.cpp b/crypto/ext4crypt/Ext4Crypt.cpp
new file mode 100644
index 0000000..9360b1a
--- /dev/null
+++ b/crypto/ext4crypt/Ext4Crypt.cpp
@@ -0,0 +1,450 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Ext4Crypt.h"
+#include "Decrypt.h"
+
+#ifdef USE_KEYSTORAGE_3
+#include "KeyStorage3.h"
+#else
+#include "KeyStorage.h"
+#endif
+#include "Utils.h"
+
+#include <algorithm>
+#include <iomanip>
+#include <map>
+#include <set>
+#include <sstream>
+#include <string>
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <openssl/sha.h>
+#include <selinux/android.h>
+#include <stdio.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <iostream>
+
+#include <private/android_filesystem_config.h>
+
+#ifdef HAVE_SYNTH_PWD_SUPPORT
+#include <ext4_utils/ext4_crypt.h>
+#else
+#include "ext4_crypt.h"
+#endif
+#ifndef HAVE_LIBKEYUTILS
+#include "key_control.h"
+#else
+#include <keyutils.h>
+#endif
+
+#include <hardware/gatekeeper.h>
+#include "HashPassword.h"
+
+#define EMULATED_USES_SELINUX 0
+#define MANAGE_MISC_DIRS 0
+
+#include <cutils/fs.h>
+#include <cutils/properties.h>
+
+#include <android-base/file.h>
+//#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+
+#define LOG(x) std::cout
+#define PLOG(x) std::cout
+#define DATA_MNT_POINT "/data"
+
+using android::base::StringPrintf;
+using android::vold::kEmptyAuthentication;
+
+// NOTE: keep in sync with StorageManager
+//static constexpr int FLAG_STORAGE_DE = 1 << 0; // moved to Decrypt.h
+//static constexpr int FLAG_STORAGE_CE = 1 << 1;
+
+// Store main DE raw ref / policy
+std::string de_raw_ref;
+// Map user ids to key references
+std::map<userid_t, std::string> s_de_key_raw_refs;
+std::map<userid_t, std::string> s_ce_key_raw_refs;
+
+namespace {
+const std::string device_key_dir = std::string() + DATA_MNT_POINT + e4crypt_unencrypted_folder;
+const std::string device_key_path = device_key_dir + "/key";
+const std::string device_key_temp = device_key_dir + "/temp";
+
+const std::string user_key_dir = std::string() + DATA_MNT_POINT + "/misc/vold/user_keys";
+const std::string user_key_temp = user_key_dir + "/temp";
+
+bool s_global_de_initialized = false;
+
+// Some users are ephemeral, don't try to wipe their keys from disk
+std::set<userid_t> s_ephemeral_users;
+
+// TODO abolish this map. Keys should not be long-lived in user memory, only kernel memory.
+// See b/26948053
+std::map<userid_t, std::string> s_ce_keys;
+
+// ext4enc:TODO get this const from somewhere good
+const int EXT4_KEY_DESCRIPTOR_SIZE = 8;
+
+// ext4enc:TODO Include structure from somewhere sensible
+// MUST be in sync with ext4_crypto.c in kernel
+constexpr int EXT4_ENCRYPTION_MODE_AES_256_XTS = 1;
+constexpr int EXT4_AES_256_XTS_KEY_SIZE = 64;
+constexpr int EXT4_MAX_KEY_SIZE = 64;
+struct ext4_encryption_key {
+ uint32_t mode;
+ char raw[EXT4_MAX_KEY_SIZE];
+ uint32_t size;
+};
+}
+
+static bool e4crypt_is_emulated() {
+ return false; //property_get_bool("persist.sys.emulate_fbe", false);
+}
+
+static const char* escape_null(const char* value) {
+ return (value == nullptr) ? "null" : value;
+}
+
+// Get raw keyref - used to make keyname and to pass to ioctl
+static std::string generate_key_ref(const char* key, int length) {
+ SHA512_CTX c;
+
+ SHA512_Init(&c);
+ SHA512_Update(&c, key, length);
+ unsigned char key_ref1[SHA512_DIGEST_LENGTH];
+ SHA512_Final(key_ref1, &c);
+
+ SHA512_Init(&c);
+ SHA512_Update(&c, key_ref1, SHA512_DIGEST_LENGTH);
+ unsigned char key_ref2[SHA512_DIGEST_LENGTH];
+ SHA512_Final(key_ref2, &c);
+
+ static_assert(EXT4_KEY_DESCRIPTOR_SIZE <= SHA512_DIGEST_LENGTH,
+ "Hash too short for descriptor");
+ return std::string((char*)key_ref2, EXT4_KEY_DESCRIPTOR_SIZE);
+}
+
+static bool fill_key(const std::string& key, ext4_encryption_key* ext4_key) {
+ if (key.size() != EXT4_AES_256_XTS_KEY_SIZE) {
+ LOG(ERROR) << "Wrong size key " << key.size();
+ return false;
+ }
+ static_assert(EXT4_AES_256_XTS_KEY_SIZE <= sizeof(ext4_key->raw), "Key too long!");
+ ext4_key->mode = EXT4_ENCRYPTION_MODE_AES_256_XTS;
+ ext4_key->size = key.size();
+ memset(ext4_key->raw, 0, sizeof(ext4_key->raw));
+ memcpy(ext4_key->raw, key.data(), key.size());
+ return true;
+}
+
+static std::string keyname(const std::string& raw_ref) {
+ std::ostringstream o;
+ o << "ext4:";
+ for (auto i : raw_ref) {
+ o << std::hex << std::setw(2) << std::setfill('0') << (int)i;
+ }
+ LOG(INFO) << "keyname is " << o.str() << "\n";
+ return o.str();
+}
+
+// Get the keyring we store all keys in
+static bool e4crypt_keyring(key_serial_t* device_keyring) {
+ *device_keyring = keyctl_search(KEY_SPEC_SESSION_KEYRING, "keyring", "e4crypt", 0);
+ if (*device_keyring == -1) {
+ PLOG(ERROR) << "Unable to find device keyring\n";
+ return false;
+ }
+ return true;
+}
+
+// Install password into global keyring
+// Return raw key reference for use in policy
+static bool install_key(const std::string& key, std::string* raw_ref) {
+ ext4_encryption_key ext4_key;
+ if (!fill_key(key, &ext4_key)) return false;
+ *raw_ref = generate_key_ref(ext4_key.raw, ext4_key.size);
+ auto ref = keyname(*raw_ref);
+ key_serial_t device_keyring;
+ if (!e4crypt_keyring(&device_keyring)) return false;
+ key_serial_t key_id =
+ add_key("logon", ref.c_str(), (void*)&ext4_key, sizeof(ext4_key), device_keyring);
+ if (key_id == -1) {
+ PLOG(ERROR) << "Failed to insert key into keyring " << device_keyring << "\n";
+ return false;
+ }
+ LOG(DEBUG) << "Added key " << key_id << " (" << ref << ") to keyring " << device_keyring
+ << " in process " << getpid() << "\n";
+ return true;
+}
+
+static std::string get_de_key_path(userid_t user_id) {
+LOG(INFO) << "get_de_key_path " << user_id << " " << StringPrintf("%s/de/%d", user_key_dir.c_str(), user_id) << "\n";
+ return StringPrintf("%s/de/%d", user_key_dir.c_str(), user_id);
+}
+
+static std::string get_ce_key_directory_path(userid_t user_id) {
+LOG(INFO) << "get_ce_key_directory_path " << user_id << ": " << StringPrintf("%s/ce/%d", user_key_dir.c_str(), user_id) << "\n";
+ return StringPrintf("%s/ce/%d", user_key_dir.c_str(), user_id);
+}
+
+// Returns the keys newest first
+static std::vector<std::string> get_ce_key_paths(const std::string& directory_path) {
+ auto dirp = std::unique_ptr<DIR, int (*)(DIR*)>(opendir(directory_path.c_str()), closedir);
+ if (!dirp) {
+ PLOG(ERROR) << "Unable to open ce key directory: " + directory_path;
+ return std::vector<std::string>();
+ }
+ std::vector<std::string> result;
+ for (;;) {
+ errno = 0;
+ auto const entry = readdir(dirp.get());
+ if (!entry) {
+ if (errno) {
+ PLOG(ERROR) << "Unable to read ce key directory: " + directory_path;
+ return std::vector<std::string>();
+ }
+ break;
+ }
+ if (entry->d_type != DT_DIR || entry->d_name[0] != 'c') {
+ LOG(DEBUG) << "Skipping non-key " << entry->d_name;
+ continue;
+ }
+ result.emplace_back(directory_path + "/" + entry->d_name);
+ LOG(INFO) << "get_ce_key_paths adding: " << directory_path + "/" + entry->d_name << "\n";
+ }
+ std::sort(result.begin(), result.end());
+ std::reverse(result.begin(), result.end());
+ return result;
+}
+
+static std::string get_ce_key_current_path(const std::string& directory_path) {
+LOG(INFO) << "get_ce_key_current_path: " << directory_path + "/current\n";
+ return directory_path + "/current";
+}
+
+// Discard all keys but the named one; rename it to canonical name.
+// No point in acting on errors in this; ignore them.
+static void fixate_user_ce_key(const std::string& directory_path, const std::string &to_fix,
+ const std::vector<std::string>& paths) {
+ for (auto const other_path: paths) {
+ if (other_path != to_fix) {
+ android::vold::destroyKey(other_path);
+ }
+ }
+ auto const current_path = get_ce_key_current_path(directory_path);
+ if (to_fix != current_path) {
+ LOG(DEBUG) << "Renaming " << to_fix << " to " << current_path;
+ if (rename(to_fix.c_str(), current_path.c_str()) != 0) {
+ PLOG(WARNING) << "Unable to rename " << to_fix << " to " << current_path;
+ }
+ }
+}
+
+static bool read_and_fixate_user_ce_key(userid_t user_id,
+ const android::vold::KeyAuthentication& auth,
+ std::string *ce_key) {
+ auto const directory_path = get_ce_key_directory_path(user_id);
+ auto const paths = get_ce_key_paths(directory_path);
+ for (auto const ce_key_path: paths) {
+ LOG(DEBUG) << "Trying user CE key " << ce_key_path;
+ if (android::vold::retrieveKey(ce_key_path, auth, ce_key)) {
+ LOG(DEBUG) << "Successfully retrieved key";
+ fixate_user_ce_key(directory_path, ce_key_path, paths);
+ return true;
+ }
+ }
+ LOG(ERROR) << "Failed to find working ce key for user " << user_id;
+ return false;
+}
+
+static bool read_and_install_user_ce_key(userid_t user_id,
+ const android::vold::KeyAuthentication& auth) {
+ if (s_ce_key_raw_refs.count(user_id) != 0) return true;
+ std::string ce_key;
+ if (!read_and_fixate_user_ce_key(user_id, auth, &ce_key)) return false;
+ std::string ce_raw_ref;
+ if (!install_key(ce_key, &ce_raw_ref)) return false;
+ s_ce_keys[user_id] = ce_key;
+ s_ce_key_raw_refs[user_id] = ce_raw_ref;
+ LOG(DEBUG) << "Installed ce key for user " << user_id;
+ return true;
+}
+
+static bool prepare_dir(const std::string& dir, mode_t mode, uid_t uid, gid_t gid) {
+ LOG(DEBUG) << "Preparing: " << dir << "\n";
+ return true;
+ return access(dir.c_str(), F_OK) == 0; // we don't want recovery creating directories or changing permissions at this point, so we will just return true if the path already exists
+ if (fs_prepare_dir(dir.c_str(), mode, uid, gid) != 0) {
+ PLOG(ERROR) << "Failed to prepare " << dir;
+ return false;
+ }
+ return true;
+}
+
+static bool path_exists(const std::string& path) {
+ return access(path.c_str(), F_OK) == 0;
+}
+
+bool lookup_key_ref(const std::map<userid_t, std::string>& key_map, userid_t user_id,
+ std::string* raw_ref) {
+ auto refi = key_map.find(user_id);
+ if (refi == key_map.end()) {
+ LOG(ERROR) << "Cannot find key for " << user_id;
+ return false;
+ }
+ *raw_ref = refi->second;
+ return true;
+}
+
+static bool is_numeric(const char* name) {
+ for (const char* p = name; *p != '\0'; p++) {
+ if (!isdigit(*p)) return false;
+ }
+ return true;
+}
+
+static bool load_all_de_keys() {
+ auto de_dir = user_key_dir + "/de";
+ auto dirp = std::unique_ptr<DIR, int (*)(DIR*)>(opendir(de_dir.c_str()), closedir);
+ if (!dirp) {
+ PLOG(ERROR) << "Unable to read de key directory";
+ return false;
+ }
+ for (;;) {
+ errno = 0;
+ auto entry = readdir(dirp.get());
+ if (!entry) {
+ if (errno) {
+ PLOG(ERROR) << "Unable to read de key directory";
+ return false;
+ }
+ break;
+ }
+ if (entry->d_type != DT_DIR || !is_numeric(entry->d_name)) {
+ LOG(DEBUG) << "Skipping non-de-key " << entry->d_name;
+ continue;
+ }
+ userid_t user_id = atoi(entry->d_name);
+ if (s_de_key_raw_refs.count(user_id) == 0) {
+ auto key_path = de_dir + "/" + entry->d_name;
+ std::string key;
+ if (!android::vold::retrieveKey(key_path, kEmptyAuthentication, &key)) return false;
+ std::string raw_ref;
+ if (!install_key(key, &raw_ref)) return false;
+ s_de_key_raw_refs[user_id] = raw_ref;
+ LOG(DEBUG) << "Installed de key for user " << user_id;
+
+ std::string user_prop = "twrp.user." + std::to_string(user_id) + ".decrypt";
+ property_set(user_prop.c_str(), "0");
+ }
+ }
+ // ext4enc:TODO: go through all DE directories, ensure that all user dirs have the
+ // correct policy set on them, and that no rogue ones exist.
+ return true;
+}
+
+bool e4crypt_initialize_global_de() {
+
+ if (s_global_de_initialized) {
+ LOG(INFO) << "Already initialized\n";
+ return true;
+ }
+
+ std::string device_key;
+ if (path_exists(device_key_path)) {
+ if (!android::vold::retrieveKey(device_key_path,
+ kEmptyAuthentication, &device_key)) return false;
+ } else {
+ LOG(INFO) << "NOT Creating new key\n";
+ return false;
+ }
+
+ std::string device_key_ref;
+ if (!install_key(device_key, &device_key_ref)) {
+ LOG(ERROR) << "Failed to install device key\n";
+ return false;
+ }
+
+ s_global_de_initialized = true;
+ de_raw_ref = device_key_ref;
+ return true;
+}
+
+bool e4crypt_init_user0() {
+ if (e4crypt_is_native()) {
+ if (!prepare_dir(user_key_dir, 0700, AID_ROOT, AID_ROOT)) return false;
+ if (!prepare_dir(user_key_dir + "/ce", 0700, AID_ROOT, AID_ROOT)) return false;
+ if (!prepare_dir(user_key_dir + "/de", 0700, AID_ROOT, AID_ROOT)) return false;
+ if (!path_exists(get_de_key_path(0))) {
+ //if (!create_and_install_user_keys(0, false)) return false;
+ printf("de key path not found\n");
+ return false;
+ }
+ // TODO: switch to loading only DE_0 here once framework makes
+ // explicit calls to install DE keys for secondary users
+ if (!load_all_de_keys()) return false;
+ }
+
+ // If this is a non-FBE device that recently left an emulated mode,
+ // restore user data directories to known-good state.
+ if (!e4crypt_is_native() && !e4crypt_is_emulated()) {
+ e4crypt_unlock_user_key(0, 0, "!", "!");
+ }
+
+ return true;
+}
+
+static bool parse_hex(const char* hex, std::string* result) {
+ if (strcmp("!", hex) == 0) {
+ *result = "";
+ return true;
+ }
+ if (android::vold::HexToStr(hex, *result) != 0) {
+ LOG(ERROR) << "Invalid FBE hex string"; // Don't log the string for security reasons
+ return false;
+ }
+ return true;
+}
+
+// TODO: rename to 'install' for consistency, and take flags to know which keys to install
+bool e4crypt_unlock_user_key(userid_t user_id, int serial __unused, const char* token_hex,
+ const char* secret_hex) {
+ if (e4crypt_is_native()) {
+ if (s_ce_key_raw_refs.count(user_id) != 0) {
+ LOG(WARNING) << "Tried to unlock already-unlocked key for user " << user_id;
+ return true;
+ }
+ std::string token, secret;
+ if (!parse_hex(token_hex, &token)) return false;
+ if (!parse_hex(secret_hex, &secret)) return false;
+ android::vold::KeyAuthentication auth(token, secret);
+ if (!read_and_install_user_ce_key(user_id, auth)) {
+ LOG(ERROR) << "Couldn't read key for " << user_id;
+ return false;
+ }
+ } else {
+ printf("Emulation mode not supported in TWRP\n");
+ }
+ return true;
+}
diff --git a/crypto/ext4crypt/Ext4Crypt.h b/crypto/ext4crypt/Ext4Crypt.h
new file mode 100644
index 0000000..beb5d8f
--- /dev/null
+++ b/crypto/ext4crypt/Ext4Crypt.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdbool.h>
+#include <sys/cdefs.h>
+
+#include <cutils/multiuser.h>
+
+#include <map>
+#include <string>
+
+__BEGIN_DECLS
+
+// General functions
+bool e4crypt_is_native();
+bool e4crypt_initialize_global_de();
+
+bool e4crypt_init_user0();
+//bool e4crypt_vold_create_user_key(userid_t user_id, int serial, bool ephemeral);
+//bool e4crypt_destroy_user_key(userid_t user_id);
+//bool e4crypt_add_user_key_auth(userid_t user_id, int serial, const char* token,
+// const char* secret);
+//bool e4crypt_fixate_newest_user_key_auth(userid_t user_id);
+
+bool e4crypt_unlock_user_key(userid_t user_id, int serial, const char* token, const char* secret);
+//bool e4crypt_lock_user_key(userid_t user_id);
+
+//bool e4crypt_prepare_user_storage(const char* volume_uuid, userid_t user_id, int serial, int flags);
+//bool e4crypt_destroy_user_storage(const char* volume_uuid, userid_t user_id, int flags);
+
+bool lookup_key_ref(const std::map<userid_t, std::string>& key_map, userid_t user_id,
+ std::string* raw_ref);
+
+__END_DECLS
diff --git a/crypto/ext4crypt/Ext4CryptPie.cpp b/crypto/ext4crypt/Ext4CryptPie.cpp
new file mode 100644
index 0000000..48b4ec9
--- /dev/null
+++ b/crypto/ext4crypt/Ext4CryptPie.cpp
@@ -0,0 +1,477 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Ext4CryptPie.h"
+
+#include "Keymaster4.h"
+#include "KeyStorage4.h"
+#include "KeyUtil.h"
+#include "Utils.h"
+#include "Decrypt.h"
+//#include "VoldUtil.h"
+
+#include <algorithm>
+#include <map>
+#include <set>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <limits.h>
+#include <selinux/android.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <private/android_filesystem_config.h>
+
+//#include "android/os/IVold.h"
+
+//#include "cryptfs.h"
+
+#define EMULATED_USES_SELINUX 0
+#define MANAGE_MISC_DIRS 0
+
+#include <cutils/fs.h>
+#include <cutils/properties.h>
+
+#include <ext4_utils/ext4_crypt.h>
+#include <keyutils.h>
+
+#include <android-base/file.h>
+//#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+
+#include <iostream>
+#define LOG(x) std::cout
+#define PLOG(x) std::cout
+#define DATA_MNT_POINT "/data"
+
+using android::base::StringPrintf;
+using android::base::WriteStringToFile;
+using android::vold::kEmptyAuthentication;
+using android::vold::KeyBuffer;
+using android::vold::Keymaster;
+using android::hardware::keymaster::V4_0::KeyFormat;
+
+// Store main DE raw ref / policy
+std::string de_raw_ref;
+// Map user ids to key references
+std::map<userid_t, std::string> s_de_key_raw_refs;
+std::map<userid_t, std::string> s_ce_key_raw_refs;
+// TODO abolish this map, per b/26948053
+std::map<userid_t, KeyBuffer> s_ce_keys;
+
+namespace {
+
+struct PolicyKeyRef {
+ std::string contents_mode;
+ std::string filenames_mode;
+ std::string key_raw_ref;
+};
+
+const std::string device_key_dir = std::string() + DATA_MNT_POINT + e4crypt_unencrypted_folder;
+const std::string device_key_path = device_key_dir + "/key";
+const std::string device_key_temp = device_key_dir + "/temp";
+
+const std::string user_key_dir = std::string() + DATA_MNT_POINT + "/misc/vold/user_keys";
+const std::string user_key_temp = user_key_dir + "/temp";
+const std::string prepare_subdirs_path = "/system/bin/vold_prepare_subdirs";
+
+const std::string systemwide_volume_key_dir =
+ std::string() + DATA_MNT_POINT + "/misc/vold/volume_keys";
+
+bool s_global_de_initialized = false;
+
+// Some users are ephemeral, don't try to wipe their keys from disk
+std::set<userid_t> s_ephemeral_users;
+
+}
+
+static bool e4crypt_is_emulated() {
+ return property_get_bool("persist.sys.emulate_fbe", false);
+}
+
+/*static const char* escape_empty(const std::string& value) {
+ return value.empty() ? "null" : value.c_str();
+}*/
+
+static std::string get_de_key_path(userid_t user_id) {
+ return StringPrintf("%s/de/%d", user_key_dir.c_str(), user_id);
+}
+
+static std::string get_ce_key_directory_path(userid_t user_id) {
+ return StringPrintf("%s/ce/%d", user_key_dir.c_str(), user_id);
+}
+
+// Returns the keys newest first
+static std::vector<std::string> get_ce_key_paths(const std::string& directory_path) {
+ auto dirp = std::unique_ptr<DIR, int (*)(DIR*)>(opendir(directory_path.c_str()), closedir);
+ if (!dirp) {
+ PLOG(ERROR) << "Unable to open ce key directory: " + directory_path << std::endl;
+ return std::vector<std::string>();
+ }
+ std::vector<std::string> result;
+ for (;;) {
+ errno = 0;
+ auto const entry = readdir(dirp.get());
+ if (!entry) {
+ if (errno) {
+ PLOG(ERROR) << "Unable to read ce key directory: " + directory_path << std::endl;
+ return std::vector<std::string>();
+ }
+ break;
+ }
+ if (entry->d_type != DT_DIR || entry->d_name[0] != 'c') {
+ LOG(DEBUG) << "Skipping non-key " << entry->d_name << std::endl;
+ continue;
+ }
+ result.emplace_back(directory_path + "/" + entry->d_name);
+ }
+ std::sort(result.begin(), result.end());
+ std::reverse(result.begin(), result.end());
+ return result;
+}
+
+static std::string get_ce_key_current_path(const std::string& directory_path) {
+ return directory_path + "/current";
+}
+
+// Discard all keys but the named one; rename it to canonical name.
+// No point in acting on errors in this; ignore them.
+static void fixate_user_ce_key(const std::string& directory_path, const std::string &to_fix,
+ const std::vector<std::string>& paths) {
+ for (auto const other_path: paths) {
+ if (other_path != to_fix) {
+ android::vold::destroyKey(other_path);
+ }
+ }
+ auto const current_path = get_ce_key_current_path(directory_path);
+ if (to_fix != current_path) {
+ LOG(DEBUG) << "Renaming " << to_fix << " to " << current_path << std::endl;
+ if (rename(to_fix.c_str(), current_path.c_str()) != 0) {
+ PLOG(WARNING) << "Unable to rename " << to_fix << " to " << current_path << std::endl;
+ }
+ }
+}
+
+static bool read_and_fixate_user_ce_key(userid_t user_id,
+ const android::vold::KeyAuthentication& auth,
+ KeyBuffer *ce_key) {
+ auto const directory_path = get_ce_key_directory_path(user_id);
+ auto const paths = get_ce_key_paths(directory_path);
+ for (auto const ce_key_path: paths) {
+ LOG(DEBUG) << "Trying user CE key " << ce_key_path << std::endl;
+ if (android::vold::retrieveKey(ce_key_path, auth, ce_key)) {
+ LOG(DEBUG) << "Successfully retrieved key" << std::endl;
+ fixate_user_ce_key(directory_path, ce_key_path, paths);
+ return true;
+ }
+ }
+ LOG(ERROR) << "Failed to find working ce key for user " << user_id << std::endl;
+ return false;
+}
+
+static bool is_wrapped_key_supported_common(const std::string& mount_point) {
+ LOG(DEBUG) << "Determining wrapped-key support for " << mount_point << std::endl;
+ std::string wrapped_key_supported = android::base::GetProperty("fbe.data.wrappedkey", "false");
+ LOG(DEBUG) << "fbe.data.wrappedkey = " << wrapped_key_supported << std::endl;
+ if (mount_point == DATA_MNT_POINT && wrapped_key_supported == "true") {
+ LOG(DEBUG) << "Wrapped key supported on " << mount_point << std::endl;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool is_wrapped_key_supported() {
+ return is_wrapped_key_supported_common(DATA_MNT_POINT);
+}
+
+bool is_wrapped_key_supported_external() {
+ return false;
+}
+
+static bool read_and_install_user_ce_key(userid_t user_id,
+ const android::vold::KeyAuthentication& auth) {
+ if (s_ce_key_raw_refs.count(user_id) != 0) return true;
+ KeyBuffer ce_key;
+ if (!read_and_fixate_user_ce_key(user_id, auth, &ce_key)) return false;
+ std::string ce_raw_ref;
+
+ if (is_wrapped_key_supported()) {
+ KeyBuffer ephemeral_wrapped_key;
+ if (!getEphemeralWrappedKey(KeyFormat::RAW, ce_key, &ephemeral_wrapped_key)) {
+ LOG(ERROR) << "Failed to export ce key";
+ return false;
+ }
+
+ ce_key = std::move(ephemeral_wrapped_key);
+ }
+ if (!android::vold::installKey(ce_key, &ce_raw_ref)) return false;
+ s_ce_keys[user_id] = std::move(ce_key);
+ s_ce_key_raw_refs[user_id] = ce_raw_ref;
+ LOG(DEBUG) << "Installed ce key for user " << user_id << std::endl;
+ return true;
+}
+
+static bool prepare_dir(const std::string& dir, mode_t mode, uid_t uid, gid_t gid) {
+ LOG(DEBUG) << "Preparing: " << dir << std::endl;
+ if (fs_prepare_dir(dir.c_str(), mode, uid, gid) != 0) {
+ PLOG(ERROR) << "Failed to prepare " << dir << std::endl;
+ return false;
+ }
+ return true;
+}
+
+// NB this assumes that there is only one thread listening for crypt commands, because
+// it creates keys in a fixed location.
+static bool create_and_install_user_keys(userid_t user_id, bool create_ephemeral) {
+ /*KeyBuffer de_key, ce_key;
+ if (!android::vold::randomKey(&de_key)) return false;
+ if (!android::vold::randomKey(&ce_key)) return false;
+ if (create_ephemeral) {
+ // If the key should be created as ephemeral, don't store it.
+ s_ephemeral_users.insert(user_id);
+ } else {
+ auto const directory_path = get_ce_key_directory_path(user_id);
+ if (!prepare_dir(directory_path, 0700, AID_ROOT, AID_ROOT)) return false;
+ auto const paths = get_ce_key_paths(directory_path);
+ std::string ce_key_path;
+ if (!get_ce_key_new_path(directory_path, paths, &ce_key_path)) return false;
+ if (!android::vold::storeKeyAtomically(ce_key_path, user_key_temp,
+ kEmptyAuthentication, ce_key)) return false;
+ fixate_user_ce_key(directory_path, ce_key_path, paths);
+ // Write DE key second; once this is written, all is good.
+ if (!android::vold::storeKeyAtomically(get_de_key_path(user_id), user_key_temp,
+ kEmptyAuthentication, de_key)) return false;
+ }
+ std::string de_raw_ref;
+ if (!android::vold::installKey(de_key, &de_raw_ref)) return false;
+ s_de_key_raw_refs[user_id] = de_raw_ref;
+ std::string ce_raw_ref;
+ if (!android::vold::installKey(ce_key, &ce_raw_ref)) return false;
+ s_ce_keys[user_id] = ce_key;
+ s_ce_key_raw_refs[user_id] = ce_raw_ref;
+ LOG(DEBUG) << "Created keys for user " << user_id;*/
+ LOG(DEBUG) << "TWRP not doing create_and_install_user_keys\n";
+ return true;
+}
+
+bool lookup_key_ref(const std::map<userid_t, std::string>& key_map, userid_t user_id,
+ std::string* raw_ref) {
+ auto refi = key_map.find(user_id);
+ if (refi == key_map.end()) {
+ LOG(ERROR) << "Cannot find key for " << user_id << std::endl;
+ return false;
+ }
+ *raw_ref = refi->second;
+ return true;
+}
+
+static void get_data_file_encryption_modes(PolicyKeyRef* key_ref) {
+ /*struct fstab_rec* rec = fs_mgr_get_entry_for_mount_point(fstab_default, DATA_MNT_POINT);
+ char const* contents_mode = strdup("ice");
+ char const* filenames_mode = strdup("aes-256-heh");
+ fs_mgr_get_file_encryption_modes(rec, &contents_mode, &filenames_mode);
+ key_ref->contents_mode = contents_mode;
+ key_ref->filenames_mode = filenames_mode;*/
+ LOG(INFO) << "contents mode '" << android::base::GetProperty("fbe.contents", "aes-256-xts") << "' filenames '" << android::base::GetProperty("fbe.filenames", "aes-256-heh") << "'\n";
+ key_ref->contents_mode =
+ android::base::GetProperty("fbe.contents", "aes-256-xts");
+ key_ref->filenames_mode =
+ android::base::GetProperty("fbe.filenames", "aes-256-heh");
+}
+
+static bool is_numeric(const char* name) {
+ for (const char* p = name; *p != '\0'; p++) {
+ if (!isdigit(*p)) return false;
+ }
+ return true;
+}
+
+static bool load_all_de_keys() {
+ auto de_dir = user_key_dir + "/de";
+ auto dirp = std::unique_ptr<DIR, int (*)(DIR*)>(opendir(de_dir.c_str()), closedir);
+ if (!dirp) {
+ PLOG(ERROR) << "Unable to read de key directory" << std::endl;
+ return false;
+ }
+ for (;;) {
+ errno = 0;
+ auto entry = readdir(dirp.get());
+ if (!entry) {
+ if (errno) {
+ PLOG(ERROR) << "Unable to read de key directory" << std::endl;
+ return false;
+ }
+ break;
+ }
+ if (entry->d_type != DT_DIR || !is_numeric(entry->d_name)) {
+ LOG(DEBUG) << "Skipping non-de-key " << entry->d_name << std::endl;
+ continue;
+ }
+ userid_t user_id = std::stoi(entry->d_name);
+ if (s_de_key_raw_refs.count(user_id) == 0) {
+ auto key_path = de_dir + "/" + entry->d_name;
+ KeyBuffer key;
+ if (!android::vold::retrieveKey(key_path, kEmptyAuthentication, &key)) return false;
+ std::string raw_ref;
+ if (is_wrapped_key_supported()) {
+ KeyBuffer ephemeral_wrapped_key;
+ if (!getEphemeralWrappedKey(KeyFormat::RAW, key, &ephemeral_wrapped_key)) {
+ LOG(ERROR) << "Failed to export de_key in create_and_install_user_keys";
+ return false;
+ }
+ key = std::move(ephemeral_wrapped_key);
+ }
+ if (!android::vold::installKey(key, &raw_ref)) return false;
+ s_de_key_raw_refs[user_id] = raw_ref;
+ LOG(DEBUG) << "Installed de key for user " << user_id << std::endl;
+
+ std::string user_prop = "twrp.user." + std::to_string(user_id) + ".decrypt";
+ property_set(user_prop.c_str(), "0");
+ }
+ }
+ // ext4enc:TODO: go through all DE directories, ensure that all user dirs have the
+ // correct policy set on them, and that no rogue ones exist.
+ return true;
+}
+
+bool e4crypt_initialize_global_de() {
+ LOG(INFO) << "e4crypt_initialize_global_de" << std::endl;
+ bool wrapped_key_supported = false;
+
+ if (s_global_de_initialized) {
+ LOG(INFO) << "Already initialized" << std::endl;
+ return true;
+ }
+
+ PolicyKeyRef device_ref;
+ wrapped_key_supported = is_wrapped_key_supported();
+ LOG(INFO) << "calling retrieveAndInstallKey\n";
+ if (!android::vold::retrieveAndInstallKey(true, kEmptyAuthentication, device_key_path,
+ device_key_temp, &device_ref.key_raw_ref, wrapped_key_supported))
+ return false;
+ get_data_file_encryption_modes(&device_ref);
+
+ std::string modestring = device_ref.contents_mode + ":" + device_ref.filenames_mode;
+ std::string mode_filename = std::string("/data") + e4crypt_key_mode;
+ if (!android::base::WriteStringToFile(modestring, mode_filename)) {
+ PLOG(ERROR) << "Cannot save type" << std::endl;
+ return false;
+ }
+
+ std::string ref_filename = std::string("/data") + e4crypt_key_ref;
+ if (!android::base::WriteStringToFile(device_ref.key_raw_ref, ref_filename)) {
+ PLOG(ERROR) << "Cannot save key reference to:" << ref_filename << std::endl;
+ return false;
+ }
+ LOG(INFO) << "Wrote system DE key reference to:" << ref_filename << std::endl;
+
+ s_global_de_initialized = true;
+ de_raw_ref = device_ref.key_raw_ref;
+ return true;
+}
+
+bool e4crypt_init_user0() {
+ LOG(DEBUG) << "e4crypt_init_user0\n";
+ if (e4crypt_is_native()) {
+ if (!prepare_dir(user_key_dir, 0700, AID_ROOT, AID_ROOT)) return false;
+ if (!prepare_dir(user_key_dir + "/ce", 0700, AID_ROOT, AID_ROOT)) return false;
+ if (!prepare_dir(user_key_dir + "/de", 0700, AID_ROOT, AID_ROOT)) return false;
+ if (!android::vold::pathExists(get_de_key_path(0))) {
+ if (!create_and_install_user_keys(0, false)) return false;
+ }
+ // TODO: switch to loading only DE_0 here once framework makes
+ // explicit calls to install DE keys for secondary users
+ if (!load_all_de_keys()) return false;
+ }
+
+ // If this is a non-FBE device that recently left an emulated mode,
+ // restore user data directories to known-good state.
+ if (!e4crypt_is_native() && !e4crypt_is_emulated()) {
+ e4crypt_unlock_user_key(0, 0, "!", "!");
+ }
+
+ return true;
+}
+
+static bool emulated_unlock(const std::string& path, mode_t mode) {
+ if (chmod(path.c_str(), mode) != 0) {
+ PLOG(ERROR) << "Failed to chmod " << path << std::endl;
+ // FIXME temporary workaround for b/26713622
+ if (e4crypt_is_emulated()) return false;
+ }
+#if EMULATED_USES_SELINUX
+ if (selinux_android_restorecon(path.c_str(), SELINUX_ANDROID_RESTORECON_FORCE) != 0) {
+ PLOG(WARNING) << "Failed to restorecon " << path << std::endl;
+ // FIXME temporary workaround for b/26713622
+ if (e4crypt_is_emulated()) return false;
+ }
+#endif
+ return true;
+}
+
+static bool parse_hex(const std::string& hex, std::string* result) {
+ if (hex == "!") {
+ *result = "";
+ return true;
+ }
+ if (android::vold::HexToStr(hex, *result) != 0) {
+ LOG(ERROR) << "Invalid FBE hex string" << std::endl; // Don't log the string for security reasons
+ return false;
+ }
+ return true;
+}
+
+// TODO: rename to 'install' for consistency, and take flags to know which keys to install
+bool e4crypt_unlock_user_key(userid_t user_id, int serial, const std::string& token_hex,
+ const std::string& secret_hex) {
+ LOG(DEBUG) << "e4crypt_unlock_user_key " << user_id << " serial=" << serial
+ << " token_present=" << (token_hex != "!") << std::endl;
+ if (e4crypt_is_native()) {
+ if (s_ce_key_raw_refs.count(user_id) != 0) {
+ LOG(WARNING) << "Tried to unlock already-unlocked key for user " << user_id << std::endl;
+ return true;
+ }
+ std::string token, secret;
+ if (!parse_hex(token_hex, &token)) return false;
+ if (!parse_hex(secret_hex, &secret)) return false;
+ android::vold::KeyAuthentication auth(token, secret);
+ if (!read_and_install_user_ce_key(user_id, auth)) {
+ LOG(ERROR) << "Couldn't read key for " << user_id << std::endl;
+ return false;
+ }
+ } else {
+ // When in emulation mode, we just use chmod. However, we also
+ // unlock directories when not in emulation mode, to bring devices
+ // back into a known-good state.
+ if (!emulated_unlock(android::vold::BuildDataSystemCePath(user_id), 0771) ||
+ !emulated_unlock(android::vold::BuildDataMiscCePath(user_id), 01771) ||
+ !emulated_unlock(android::vold::BuildDataMediaCePath("", user_id), 0770) ||
+ !emulated_unlock(android::vold::BuildDataUserCePath("", user_id), 0771)) {
+ LOG(ERROR) << "Failed to unlock user " << user_id << std::endl;
+ return false;
+ }
+ }
+ return true;
+}
diff --git a/crypto/ext4crypt/Ext4CryptPie.h b/crypto/ext4crypt/Ext4CryptPie.h
new file mode 100644
index 0000000..7236bc0
--- /dev/null
+++ b/crypto/ext4crypt/Ext4CryptPie.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <map>
+#include <string>
+
+#include <cutils/multiuser.h>
+
+bool e4crypt_initialize_global_de();
+
+bool e4crypt_init_user0();
+/*bool e4crypt_vold_create_user_key(userid_t user_id, int serial, bool ephemeral);
+bool e4crypt_destroy_user_key(userid_t user_id);
+bool e4crypt_add_user_key_auth(userid_t user_id, int serial, const std::string& token,
+ const std::string& secret);
+bool e4crypt_fixate_newest_user_key_auth(userid_t user_id);*/
+
+bool e4crypt_unlock_user_key(userid_t user_id, int serial, const std::string& token,
+ const std::string& secret);
+//bool e4crypt_lock_user_key(userid_t user_id);
+
+/*bool e4crypt_prepare_user_storage(const std::string& volume_uuid, userid_t user_id, int serial,
+ int flags);*/
+/*bool e4crypt_destroy_user_storage(const std::string& volume_uuid, userid_t user_id, int flags);
+
+bool e4crypt_destroy_volume_keys(const std::string& volume_uuid);*/
+
+bool is_wrapped_key_supported();
+bool is_wrapped_key_supported_external();
+
+bool lookup_key_ref(const std::map<userid_t, std::string>& key_map, userid_t user_id,
+ std::string* raw_ref);
diff --git a/crypto/ext4crypt/HashPassword.cpp b/crypto/ext4crypt/HashPassword.cpp
new file mode 100644
index 0000000..07ecb1f
--- /dev/null
+++ b/crypto/ext4crypt/HashPassword.cpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2016 Team Win Recovery Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * This computes the "secret" used by Android as one of the parameters
+ * to decrypt File Based Encryption. The secret is prefixed with
+ * "Android FBE credential hash" padded with 0s to 128 bytes then the
+ * user's password is appended to the end of the 128 bytes. This string
+ * is then hashed with sha512 and the sha512 value is then converted to
+ * hex with upper-case characters.
+ */
+
+#include <stdio.h>
+#include <string>
+#include <stdlib.h>
+#include <openssl/sha.h>
+#include <openssl/hmac.h>
+
+#include "HashPassword.h"
+
+#define PASS_PADDING_SIZE 128
+#define SHA512_HEX_SIZE SHA512_DIGEST_LENGTH * 2
+#define SHA256_HEX_SIZE SHA256_DIGEST_LENGTH * 2
+
+void* PersonalizedHashBinary(const char* prefix, const char* key, const size_t key_size) {
+ size_t size = PASS_PADDING_SIZE + key_size;
+ unsigned char* buffer = (unsigned char*)calloc(1, size);
+ if (!buffer) return NULL; // failed to malloc
+ memcpy((void*)buffer, (void*)prefix, strlen(prefix));
+ unsigned char* ptr = buffer + PASS_PADDING_SIZE;
+ memcpy((void*)ptr, key, key_size);
+ unsigned char hash[SHA512_DIGEST_LENGTH];
+ SHA512_CTX sha512;
+ SHA512_Init(&sha512);
+ SHA512_Update(&sha512, buffer, size);
+ SHA512_Final(hash, &sha512);
+ free(buffer);
+ void* ret = malloc(SHA512_DIGEST_LENGTH);
+ if (!ret) return NULL; // failed to malloc
+ memcpy(ret, (void*)&hash[0], SHA512_DIGEST_LENGTH);
+ return ret;
+}
+
+std::string PersonalizedHash(const char* prefix, const char* key, const size_t key_size) {
+ size_t size = PASS_PADDING_SIZE + key_size;
+ unsigned char* buffer = (unsigned char*)calloc(1, size);
+ if (!buffer) return ""; // failed to malloc
+ memcpy((void*)buffer, (void*)prefix, strlen(prefix));
+ unsigned char* ptr = buffer + PASS_PADDING_SIZE;
+ memcpy((void*)ptr, key, key_size);
+ unsigned char hash[SHA512_DIGEST_LENGTH];
+ SHA512_CTX sha512;
+ SHA512_Init(&sha512);
+ SHA512_Update(&sha512, buffer, size);
+ SHA512_Final(hash, &sha512);
+ int index = 0;
+ char hex_hash[SHA512_HEX_SIZE + 1];
+ for(index = 0; index < SHA512_DIGEST_LENGTH; index++)
+ sprintf(hex_hash + (index * 2), "%02X", hash[index]);
+ hex_hash[128] = 0;
+ std::string ret = hex_hash;
+ free(buffer);
+ return ret;
+}
+
+std::string PersonalizedHash(const char* prefix, const std::string& Password) {
+ return PersonalizedHash(prefix, Password.c_str(), Password.size());
+}
+
+std::string PersonalizedHashSP800(const char* label, const char* context, const char* key, const size_t key_size) {
+ HMAC_CTX ctx;
+ HMAC_CTX_init(&ctx);
+ HMAC_Init_ex(&ctx, key, key_size, EVP_sha256(), NULL);
+ unsigned int counter = 1;
+ endianswap(&counter);
+ HMAC_Update(&ctx, (const unsigned char*)&counter, 4);
+ HMAC_Update(&ctx, (const unsigned char*)label, strlen(label));
+ const unsigned char divider = 0;
+ HMAC_Update(&ctx, ÷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/ext4crypt/HashPassword.h b/crypto/ext4crypt/HashPassword.h
new file mode 100644
index 0000000..73880b1
--- /dev/null
+++ b/crypto/ext4crypt/HashPassword.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2016 Team Win Recovery Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __HASH_PASSWORD_H
+#define __HASH_PASSWORD_H
+
+#include <string>
+
+#define FBE_PERSONALIZATION "Android FBE credential hash"
+#define PERSONALISATION_WEAVER_KEY "weaver-key"
+#define PERSONALISATION_WEAVER_PASSWORD "weaver-pwd"
+#define PERSONALISATION_APPLICATION_ID "application-id"
+#define PERSONALIZATION_FBE_KEY "fbe-key"
+#define PERSONALIZATION_USER_GK_AUTH "user-gk-authentication"
+#define PERSONALISATION_SECDISCARDABLE "secdiscardable-transform"
+#define PERSONALISATION_CONTEXT "android-synthetic-password-personalization-context"
+
+void* PersonalizedHashBinary(const char* prefix, const char* key, const size_t key_size);
+
+std::string PersonalizedHash(const char* prefix, const char* key, const size_t key_size);
+std::string PersonalizedHash(const char* prefix, const std::string& Password);
+std::string PersonalizedHashSP800(const char* label, const char* context, const char* key, const size_t key_size);
+std::string HashPassword(const std::string& Password);
+
+template <class T>
+void endianswap(T *objp) {
+ unsigned char *memp = reinterpret_cast<unsigned char*>(objp);
+ std::reverse(memp, memp + sizeof(T));
+}
+
+#endif
diff --git a/crypto/ext4crypt/KeyBuffer.cpp b/crypto/ext4crypt/KeyBuffer.cpp
new file mode 100644
index 0000000..e7aede5
--- /dev/null
+++ b/crypto/ext4crypt/KeyBuffer.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "KeyBuffer.h"
+
+#include <algorithm>
+#include <cstring>
+
+namespace android {
+namespace vold {
+
+KeyBuffer operator+(KeyBuffer&& lhs, const KeyBuffer& rhs) {
+ std::copy(rhs.begin(), rhs.end(), std::back_inserter(lhs));
+ return std::move(lhs);
+}
+
+KeyBuffer operator+(KeyBuffer&& lhs, const char* rhs) {
+ std::copy(rhs, rhs + strlen(rhs), std::back_inserter(lhs));
+ return std::move(lhs);
+}
+
+} // namespace vold
+} // namespace android
+
diff --git a/crypto/ext4crypt/KeyBuffer.h b/crypto/ext4crypt/KeyBuffer.h
new file mode 100644
index 0000000..2087187
--- /dev/null
+++ b/crypto/ext4crypt/KeyBuffer.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_VOLD_KEYBUFFER_H
+#define ANDROID_VOLD_KEYBUFFER_H
+
+#include <cstring>
+#include <memory>
+#include <vector>
+
+namespace android {
+namespace vold {
+
+/**
+ * Variant of memset() that should never be optimized away. Borrowed from keymaster code.
+ */
+#ifdef __clang__
+#define OPTNONE __attribute__((optnone))
+#else // not __clang__
+#define OPTNONE __attribute__((optimize("O0")))
+#endif // not __clang__
+inline OPTNONE void* memset_s(void* s, int c, size_t n) {
+ if (!s)
+ return s;
+ return memset(s, c, n);
+}
+#undef OPTNONE
+
+// Allocator that delegates useful work to standard one but zeroes data before deallocating.
+class ZeroingAllocator : public std::allocator<char> {
+ public:
+ void deallocate(pointer p, size_type n)
+ {
+ memset_s(p, 0, n);
+ std::allocator<char>::deallocate(p, n);
+ }
+};
+
+// Char vector that zeroes memory when deallocating.
+using KeyBuffer = std::vector<char, ZeroingAllocator>;
+
+// Convenience methods to concatenate key buffers.
+KeyBuffer operator+(KeyBuffer&& lhs, const KeyBuffer& rhs);
+KeyBuffer operator+(KeyBuffer&& lhs, const char* rhs);
+
+} // namespace vold
+} // namespace android
+
+#endif
+
diff --git a/crypto/ext4crypt/KeyStorage.cpp b/crypto/ext4crypt/KeyStorage.cpp
new file mode 100644
index 0000000..199520e
--- /dev/null
+++ b/crypto/ext4crypt/KeyStorage.cpp
@@ -0,0 +1,349 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "KeyStorage.h"
+
+#include "Keymaster.h"
+#include "ScryptParameters.h"
+#include "Utils.h"
+
+#include <vector>
+
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <iostream>
+
+#include <openssl/sha.h>
+
+#include <android-base/file.h>
+//#include <android-base/logging.h>
+
+#include <cutils/properties.h>
+
+#include <hardware/hw_auth_token.h>
+
+#include <keymaster/authorization_set.h>
+
+extern "C" {
+
+#include "crypto_scrypt.h"
+}
+
+#define ERROR 1
+#define LOG(x) std::cout
+#define PLOG(x) std::cout
+
+namespace android {
+namespace vold {
+
+const KeyAuthentication kEmptyAuthentication{"", ""};
+
+static constexpr size_t AES_KEY_BYTES = 32;
+static constexpr size_t GCM_NONCE_BYTES = 12;
+static constexpr size_t GCM_MAC_BYTES = 16;
+static constexpr size_t SALT_BYTES = 1 << 4;
+static constexpr size_t SECDISCARDABLE_BYTES = 1 << 14;
+static constexpr size_t STRETCHED_BYTES = 1 << 6;
+
+static constexpr uint32_t AUTH_TIMEOUT = 30; // Seconds
+
+static const char* kCurrentVersion = "1";
+static const char* kRmPath = "/system/bin/rm";
+static const char* kSecdiscardPath = "/system/bin/secdiscard";
+static const char* kStretch_none = "none";
+static const char* kStretch_nopassword = "nopassword";
+static const std::string kStretchPrefix_scrypt = "scrypt ";
+static const char* kFn_encrypted_key = "encrypted_key";
+static const char* kFn_keymaster_key_blob = "keymaster_key_blob";
+static const char* kFn_salt = "salt";
+static const char* kFn_secdiscardable = "secdiscardable";
+static const char* kFn_stretching = "stretching";
+static const char* kFn_version = "version";
+
+static bool checkSize(const std::string& kind, size_t actual, size_t expected) {
+ if (actual != expected) {
+ LOG(ERROR) << "Wrong number of bytes in " << kind << ", expected " << expected << " got "
+ << actual;
+ return false;
+ }
+ return true;
+}
+
+static std::string hashSecdiscardable(const std::string& secdiscardable) {
+ SHA512_CTX c;
+
+ SHA512_Init(&c);
+ // Personalise the hashing by introducing a fixed prefix.
+ // Hashing applications should use personalization except when there is a
+ // specific reason not to; see section 4.11 of https://www.schneier.com/skein1.3.pdf
+ std::string secdiscardableHashingPrefix = "Android secdiscardable SHA512";
+ secdiscardableHashingPrefix.resize(SHA512_CBLOCK);
+ SHA512_Update(&c, secdiscardableHashingPrefix.data(), secdiscardableHashingPrefix.size());
+ SHA512_Update(&c, secdiscardable.data(), secdiscardable.size());
+ std::string res(SHA512_DIGEST_LENGTH, '\0');
+ SHA512_Final(reinterpret_cast<uint8_t*>(&res[0]), &c);
+ return res;
+}
+
+/*static bool generateKeymasterKey(Keymaster& keymaster, const KeyAuthentication& auth,
+ const std::string& appId, std::string* key) {
+ auto paramBuilder = keymaster::AuthorizationSetBuilder()
+ .AesEncryptionKey(AES_KEY_BYTES * 8)
+ .Authorization(keymaster::TAG_BLOCK_MODE, KM_MODE_GCM)
+ .Authorization(keymaster::TAG_MIN_MAC_LENGTH, GCM_MAC_BYTES * 8)
+ .Authorization(keymaster::TAG_PADDING, KM_PAD_NONE);
+ addStringParam(¶mBuilder, keymaster::TAG_APPLICATION_ID, appId);
+ if (auth.token.empty()) {
+ LOG(DEBUG) << "Creating key that doesn't need auth token";
+ paramBuilder.Authorization(keymaster::TAG_NO_AUTH_REQUIRED);
+ } else {
+ LOG(DEBUG) << "Auth token required for key";
+ if (auth.token.size() != sizeof(hw_auth_token_t)) {
+ LOG(ERROR) << "Auth token should be " << sizeof(hw_auth_token_t) << " bytes, was "
+ << auth.token.size() << " bytes";
+ return false;
+ }
+ const hw_auth_token_t* at = reinterpret_cast<const hw_auth_token_t*>(auth.token.data());
+ paramBuilder.Authorization(keymaster::TAG_USER_SECURE_ID, at->user_id);
+ paramBuilder.Authorization(keymaster::TAG_USER_AUTH_TYPE, HW_AUTH_PASSWORD);
+ paramBuilder.Authorization(keymaster::TAG_AUTH_TIMEOUT, AUTH_TIMEOUT);
+ }
+ return keymaster.generateKey(paramBuilder.build(), key);
+}*/
+
+static keymaster::AuthorizationSetBuilder beginParams(const KeyAuthentication& auth,
+ const std::string& appId) {
+ auto paramBuilder = keymaster::AuthorizationSetBuilder()
+ .Authorization(keymaster::TAG_BLOCK_MODE, KM_MODE_GCM)
+ .Authorization(keymaster::TAG_MAC_LENGTH, GCM_MAC_BYTES * 8)
+ .Authorization(keymaster::TAG_PADDING, KM_PAD_NONE);
+ addStringParam(¶mBuilder, keymaster::TAG_APPLICATION_ID, appId);
+ if (!auth.token.empty()) {
+ LOG(DEBUG) << "Supplying auth token to Keymaster";
+ addStringParam(¶mBuilder, keymaster::TAG_AUTH_TOKEN, auth.token);
+ }
+ return paramBuilder;
+}
+
+/*static bool encryptWithKeymasterKey(Keymaster& keymaster, const std::string& key,
+ const KeyAuthentication& auth, const std::string& appId,
+ const std::string& message, std::string* ciphertext) {
+ auto params = beginParams(auth, appId).build();
+ keymaster::AuthorizationSet outParams;
+ auto opHandle = keymaster.begin(KM_PURPOSE_ENCRYPT, key, params, &outParams);
+ if (!opHandle) return false;
+ keymaster_blob_t nonceBlob;
+ if (!outParams.GetTagValue(keymaster::TAG_NONCE, &nonceBlob)) {
+ LOG(ERROR) << "GCM encryption but no nonce generated";
+ return false;
+ }
+ // nonceBlob here is just a pointer into existing data, must not be freed
+ std::string nonce(reinterpret_cast<const char*>(nonceBlob.data), nonceBlob.data_length);
+ if (!checkSize("nonce", nonce.size(), GCM_NONCE_BYTES)) return false;
+ std::string body;
+ if (!opHandle.updateCompletely(message, &body)) return false;
+
+ std::string mac;
+ if (!opHandle.finishWithOutput(&mac)) return false;
+ if (!checkSize("mac", mac.size(), GCM_MAC_BYTES)) return false;
+ *ciphertext = nonce + body + mac;
+ return true;
+}*/
+
+static bool decryptWithKeymasterKey(Keymaster& keymaster, const std::string& key,
+ const KeyAuthentication& auth, const std::string& appId,
+ const std::string& ciphertext, std::string* message) {
+ auto nonce = ciphertext.substr(0, GCM_NONCE_BYTES);
+ auto bodyAndMac = ciphertext.substr(GCM_NONCE_BYTES);
+ auto params = addStringParam(beginParams(auth, appId), keymaster::TAG_NONCE, nonce).build();
+ auto opHandle = keymaster.begin(KM_PURPOSE_DECRYPT, key, params);
+ if (!opHandle) return false;
+ if (!opHandle.updateCompletely(bodyAndMac, message)) return false;
+ if (!opHandle.finish()) return false;
+ return true;
+}
+
+static bool readFileToString(const std::string& filename, std::string* result) {
+ if (!android::base::ReadFileToString(filename, result)) {
+ PLOG(ERROR) << "Failed to read from " << filename;
+ return false;
+ }
+ return true;
+}
+
+/*static bool writeStringToFile(const std::string& payload, const std::string& filename) {
+ if (!android::base::WriteStringToFile(payload, filename)) {
+ PLOG(ERROR) << "Failed to write to " << filename;
+ return false;
+ }
+ return true;
+}*/
+
+static std::string getStretching() {
+ char paramstr[PROPERTY_VALUE_MAX];
+
+ property_get(SCRYPT_PROP, paramstr, SCRYPT_DEFAULTS);
+ return std::string() + kStretchPrefix_scrypt + paramstr;
+}
+
+static bool stretchingNeedsSalt(const std::string& stretching) {
+ return stretching != kStretch_nopassword && stretching != kStretch_none;
+}
+
+static bool stretchSecret(const std::string& stretching, const std::string& secret,
+ const std::string& salt, std::string* stretched) {
+ if (stretching == kStretch_nopassword) {
+ if (!secret.empty()) {
+ LOG(WARNING) << "Password present but stretching is nopassword";
+ // Continue anyway
+ }
+ stretched->clear();
+ } else if (stretching == kStretch_none) {
+ *stretched = secret;
+ } else if (std::equal(kStretchPrefix_scrypt.begin(), kStretchPrefix_scrypt.end(),
+ stretching.begin())) {
+ int Nf, rf, pf;
+ if (!parse_scrypt_parameters(stretching.substr(kStretchPrefix_scrypt.size()).c_str(), &Nf,
+ &rf, &pf)) {
+ LOG(ERROR) << "Unable to parse scrypt params in stretching: " << stretching;
+ return false;
+ }
+ stretched->assign(STRETCHED_BYTES, '\0');
+ if (crypto_scrypt(reinterpret_cast<const uint8_t*>(secret.data()), secret.size(),
+ reinterpret_cast<const uint8_t*>(salt.data()), salt.size(),
+ 1 << Nf, 1 << rf, 1 << pf,
+ reinterpret_cast<uint8_t*>(&(*stretched)[0]), stretched->size()) != 0) {
+ LOG(ERROR) << "scrypt failed with params: " << stretching;
+ return false;
+ }
+ } else {
+ LOG(ERROR) << "Unknown stretching type: " << stretching;
+ return false;
+ }
+ return true;
+}
+
+static bool generateAppId(const KeyAuthentication& auth, const std::string& stretching,
+ const std::string& salt, const std::string& secdiscardable,
+ std::string* appId) {
+ std::string stretched;
+ if (!stretchSecret(stretching, auth.secret, salt, &stretched)) return false;
+ *appId = hashSecdiscardable(secdiscardable) + stretched;
+ return true;
+}
+
+/*bool storeKey(const std::string& dir, const KeyAuthentication& auth, const std::string& key) {
+ if (TEMP_FAILURE_RETRY(mkdir(dir.c_str(), 0700)) == -1) {
+ PLOG(ERROR) << "key mkdir " << dir;
+ return false;
+ }
+ if (!writeStringToFile(kCurrentVersion, dir + "/" + kFn_version)) return false;
+ std::string secdiscardable;
+ if (ReadRandomBytes(SECDISCARDABLE_BYTES, secdiscardable) != OK) {
+ // TODO status_t plays badly with PLOG, fix it.
+ LOG(ERROR) << "Random read failed";
+ return false;
+ }
+ if (!writeStringToFile(secdiscardable, dir + "/" + kFn_secdiscardable)) return false;
+ std::string stretching = auth.secret.empty() ? kStretch_nopassword : getStretching();
+ if (!writeStringToFile(stretching, dir + "/" + kFn_stretching)) return false;
+ std::string salt;
+ if (stretchingNeedsSalt(stretching)) {
+ if (ReadRandomBytes(SALT_BYTES, salt) != OK) {
+ LOG(ERROR) << "Random read failed";
+ return false;
+ }
+ if (!writeStringToFile(salt, dir + "/" + kFn_salt)) return false;
+ }
+ std::string appId;
+ if (!generateAppId(auth, stretching, salt, secdiscardable, &appId)) return false;
+ Keymaster keymaster;
+ if (!keymaster) return false;
+ std::string kmKey;
+ if (!generateKeymasterKey(keymaster, auth, appId, &kmKey)) return false;
+ if (!writeStringToFile(kmKey, dir + "/" + kFn_keymaster_key_blob)) return false;
+ std::string encryptedKey;
+ if (!encryptWithKeymasterKey(keymaster, kmKey, auth, appId, key, &encryptedKey)) return false;
+ if (!writeStringToFile(encryptedKey, dir + "/" + kFn_encrypted_key)) return false;
+ return true;
+}*/
+
+bool retrieveKey(const std::string& dir, const KeyAuthentication& auth, std::string* key) {
+ std::string version;
+ if (!readFileToString(dir + "/" + kFn_version, &version)) return false;
+ if (version != kCurrentVersion) {
+ LOG(ERROR) << "Version mismatch, expected " << kCurrentVersion << " got " << version;
+ return false;
+ }
+ std::string secdiscardable;
+ if (!readFileToString(dir + "/" + kFn_secdiscardable, &secdiscardable)) return false;
+ std::string stretching;
+ if (!readFileToString(dir + "/" + kFn_stretching, &stretching)) return false;
+ std::string salt;
+ if (stretchingNeedsSalt(stretching)) {
+ if (!readFileToString(dir + "/" + kFn_salt, &salt)) return false;
+ }
+ std::string appId;
+ if (!generateAppId(auth, stretching, salt, secdiscardable, &appId)) return false;
+ std::string kmKey;
+ if (!readFileToString(dir + "/" + kFn_keymaster_key_blob, &kmKey)) return false;
+ std::string encryptedMessage;
+ if (!readFileToString(dir + "/" + kFn_encrypted_key, &encryptedMessage)) return false;
+ Keymaster keymaster;
+ if (!keymaster) return false;
+ return decryptWithKeymasterKey(keymaster, kmKey, auth, appId, encryptedMessage, key);
+}
+
+static bool deleteKey(const std::string& dir) {
+ std::string kmKey;
+ if (!readFileToString(dir + "/" + kFn_keymaster_key_blob, &kmKey)) return false;
+ Keymaster keymaster;
+ if (!keymaster) return false;
+ if (!keymaster.deleteKey(kmKey)) return false;
+ return true;
+}
+
+static bool secdiscardSecdiscardable(const std::string& dir) {
+ if (ForkExecvp(
+ std::vector<std::string>{kSecdiscardPath, "--", dir + "/" + kFn_secdiscardable}) != 0) {
+ LOG(ERROR) << "secdiscard failed";
+ return false;
+ }
+ return true;
+}
+
+static bool recursiveDeleteKey(const std::string& dir) {
+ if (ForkExecvp(std::vector<std::string>{kRmPath, "-rf", dir}) != 0) {
+ LOG(ERROR) << "recursive delete failed";
+ return false;
+ }
+ return true;
+}
+
+bool destroyKey(const std::string& dir) {
+ bool success = true;
+ // Try each thing, even if previous things failed.
+ success &= deleteKey(dir);
+ success &= secdiscardSecdiscardable(dir);
+ success &= recursiveDeleteKey(dir);
+ return success;
+}
+
+} // namespace vold
+} // namespace android
diff --git a/crypto/ext4crypt/KeyStorage.h b/crypto/ext4crypt/KeyStorage.h
new file mode 100644
index 0000000..63d38da
--- /dev/null
+++ b/crypto/ext4crypt/KeyStorage.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_VOLD_KEYSTORAGE_H
+#define ANDROID_VOLD_KEYSTORAGE_H
+
+#include <string>
+
+namespace android {
+namespace vold {
+
+// Represents the information needed to decrypt a disk encryption key.
+// If "token" is nonempty, it is passed in as a required Gatekeeper auth token.
+// If "secret" is nonempty, it is appended to the application-specific
+// binary needed to unlock.
+class KeyAuthentication {
+ public:
+ KeyAuthentication(std::string t, std::string s) : token{t}, secret{s} {};
+ const std::string token;
+ const std::string secret;
+};
+
+extern const KeyAuthentication kEmptyAuthentication;
+
+// Create a directory at the named path, and store "key" in it,
+// in such a way that it can only be retrieved via Keymaster and
+// can be securely deleted.
+// It's safe to move/rename the directory after creation.
+//bool storeKey(const std::string& dir, const KeyAuthentication& auth, const std::string& key);
+
+// Retrieve the key from the named directory.
+bool retrieveKey(const std::string& dir, const KeyAuthentication& auth, std::string* key);
+
+// Securely destroy the key stored in the named directory and delete the directory.
+bool destroyKey(const std::string& dir);
+
+} // namespace vold
+} // namespace android
+
+#endif
diff --git a/crypto/ext4crypt/KeyStorage3.cpp b/crypto/ext4crypt/KeyStorage3.cpp
new file mode 100644
index 0000000..a07212d
--- /dev/null
+++ b/crypto/ext4crypt/KeyStorage3.cpp
@@ -0,0 +1,526 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "KeyStorage3.h"
+
+#include "Keymaster3.h"
+#include "ScryptParameters.h"
+#include "Utils.h"
+
+#include <vector>
+
+#include <errno.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/sha.h>
+
+#include <android-base/file.h>
+//#include <android-base/logging.h>
+
+#include <cutils/properties.h>
+
+#include <hardware/hw_auth_token.h>
+
+#include <keystore/authorization_set.h>
+#include <keystore/keystore_hidl_support.h>
+
+extern "C" {
+
+#include "crypto_scrypt.h"
+}
+
+#include <iostream>
+#define ERROR 1
+#define LOG(x) std::cout
+#define PLOG(x) std::cout
+
+namespace android {
+namespace vold {
+using namespace keystore;
+
+const KeyAuthentication kEmptyAuthentication{"", ""};
+
+static constexpr size_t AES_KEY_BYTES = 32;
+static constexpr size_t GCM_NONCE_BYTES = 12;
+static constexpr size_t GCM_MAC_BYTES = 16;
+static constexpr size_t SALT_BYTES = 1 << 4;
+static constexpr size_t SECDISCARDABLE_BYTES = 1 << 14;
+static constexpr size_t STRETCHED_BYTES = 1 << 6;
+
+static constexpr uint32_t AUTH_TIMEOUT = 30; // Seconds
+
+static const char* kCurrentVersion = "1";
+static const char* kRmPath = "/system/bin/rm";
+static const char* kSecdiscardPath = "/system/bin/secdiscard";
+static const char* kStretch_none = "none";
+static const char* kStretch_nopassword = "nopassword";
+static const std::string kStretchPrefix_scrypt = "scrypt ";
+static const char* kHashPrefix_secdiscardable = "Android secdiscardable SHA512";
+static const char* kHashPrefix_keygen = "Android key wrapping key generation SHA512";
+static const char* kFn_encrypted_key = "encrypted_key";
+static const char* kFn_keymaster_key_blob = "keymaster_key_blob";
+static const char* kFn_keymaster_key_blob_upgraded = "keymaster_key_blob_upgraded";
+static const char* kFn_salt = "salt";
+static const char* kFn_secdiscardable = "secdiscardable";
+static const char* kFn_stretching = "stretching";
+static const char* kFn_version = "version";
+
+static bool checkSize(const std::string& kind, size_t actual, size_t expected) {
+ if (actual != expected) {
+ LOG(ERROR) << "Wrong number of bytes in " << kind << ", expected " << expected << " got "
+ << actual;
+ return false;
+ }
+ return true;
+}
+
+static std::string hashWithPrefix(char const* prefix, const std::string& tohash) {
+ SHA512_CTX c;
+
+ SHA512_Init(&c);
+ // Personalise the hashing by introducing a fixed prefix.
+ // Hashing applications should use personalization except when there is a
+ // specific reason not to; see section 4.11 of https://www.schneier.com/skein1.3.pdf
+ std::string hashingPrefix = prefix;
+ hashingPrefix.resize(SHA512_CBLOCK);
+ SHA512_Update(&c, hashingPrefix.data(), hashingPrefix.size());
+ SHA512_Update(&c, tohash.data(), tohash.size());
+ std::string res(SHA512_DIGEST_LENGTH, '\0');
+ SHA512_Final(reinterpret_cast<uint8_t*>(&res[0]), &c);
+ return res;
+}
+
+/*static bool generateKeymasterKey(Keymaster& keymaster, const KeyAuthentication& auth,
+ const std::string& appId, std::string* key) {
+ auto paramBuilder = AuthorizationSetBuilder()
+ .AesEncryptionKey(AES_KEY_BYTES * 8)
+ .Authorization(TAG_BLOCK_MODE, BlockMode::GCM)
+ .Authorization(TAG_MIN_MAC_LENGTH, GCM_MAC_BYTES * 8)
+ .Authorization(TAG_PADDING, PaddingMode::NONE)
+ .Authorization(TAG_APPLICATION_ID, blob2hidlVec(appId));
+ if (auth.token.empty()) {
+ LOG(DEBUG) << "Creating key that doesn't need auth token";
+ paramBuilder.Authorization(TAG_NO_AUTH_REQUIRED);
+ } else {
+ LOG(DEBUG) << "Auth token required for key";
+ if (auth.token.size() != sizeof(hw_auth_token_t)) {
+ LOG(ERROR) << "Auth token should be " << sizeof(hw_auth_token_t) << " bytes, was "
+ << auth.token.size() << " bytes";
+ return false;
+ }
+ const hw_auth_token_t* at = reinterpret_cast<const hw_auth_token_t*>(auth.token.data());
+ paramBuilder.Authorization(TAG_USER_SECURE_ID, at->user_id);
+ paramBuilder.Authorization(TAG_USER_AUTH_TYPE, HardwareAuthenticatorType::PASSWORD);
+ paramBuilder.Authorization(TAG_AUTH_TIMEOUT, AUTH_TIMEOUT);
+ }
+ return keymaster.generateKey(paramBuilder, key);
+}*/
+
+static AuthorizationSet beginParams(const KeyAuthentication& auth,
+ const std::string& appId) {
+ auto paramBuilder = AuthorizationSetBuilder()
+ .Authorization(TAG_BLOCK_MODE, BlockMode::GCM)
+ .Authorization(TAG_MAC_LENGTH, GCM_MAC_BYTES * 8)
+ .Authorization(TAG_PADDING, PaddingMode::NONE)
+ .Authorization(TAG_APPLICATION_ID, blob2hidlVec(appId));
+ if (!auth.token.empty()) {
+ LOG(DEBUG) << "Supplying auth token to Keymaster";
+ paramBuilder.Authorization(TAG_AUTH_TOKEN, blob2hidlVec(auth.token));
+ }
+ return paramBuilder;
+}
+
+static bool readFileToString(const std::string& filename, std::string* result) {
+ if (!android::base::ReadFileToString(filename, result)) {
+ PLOG(ERROR) << "Failed to read from " << filename;
+ return false;
+ }
+ return true;
+}
+
+static bool writeStringToFile(const std::string& payload, const std::string& filename) {
+ if (!android::base::WriteStringToFile(payload, filename)) {
+ PLOG(ERROR) << "Failed to write to " << filename;
+ return false;
+ }
+ return true;
+}
+
+static KeymasterOperation begin(Keymaster& keymaster, const std::string& dir,
+ KeyPurpose purpose,
+ const AuthorizationSet &keyParams,
+ const AuthorizationSet &opParams,
+ AuthorizationSet* outParams) {
+ auto kmKeyPath = dir + "/" + kFn_keymaster_key_blob;
+ std::string kmKey;
+ if (!readFileToString(kmKeyPath, &kmKey)) return KeymasterOperation();
+ AuthorizationSet inParams(keyParams);
+ inParams.append(opParams.begin(), opParams.end());
+ for (;;) {
+ auto opHandle = keymaster.begin(purpose, kmKey, inParams, outParams);
+ if (opHandle) {
+ return opHandle;
+ }
+ if (opHandle.errorCode() != ErrorCode::KEY_REQUIRES_UPGRADE) return opHandle;
+ LOG(DEBUG) << "Upgrading key: " << dir;
+ std::string newKey;
+ if (!keymaster.upgradeKey(kmKey, keyParams, &newKey)) return KeymasterOperation();
+ // Upgrade the key in memory but do not replace the key in storage
+ /*auto newKeyPath = dir + "/" + kFn_keymaster_key_blob_upgraded;
+ if (!writeStringToFile(newKey, newKeyPath)) return KeymasterOperation();
+ if (rename(newKeyPath.c_str(), kmKeyPath.c_str()) != 0) {
+ PLOG(ERROR) << "Unable to move upgraded key to location: " << kmKeyPath;
+ return KeymasterOperation();
+ }
+ if (!keymaster.deleteKey(kmKey)) {
+ LOG(ERROR) << "Key deletion failed during upgrade, continuing anyway: " << dir;
+ }*/
+ kmKey = newKey;
+ LOG(INFO) << "Key upgraded: " << dir;
+ }
+}
+
+/*static bool encryptWithKeymasterKey(Keymaster& keymaster, const std::string& dir,
+ const AuthorizationSet &keyParams,
+ const std::string& message, std::string* ciphertext) {
+ AuthorizationSet opParams;
+ AuthorizationSet outParams;
+ auto opHandle = begin(keymaster, dir, KeyPurpose::ENCRYPT, keyParams, opParams, &outParams);
+ if (!opHandle) return false;
+ auto nonceBlob = outParams.GetTagValue(TAG_NONCE);
+ if (!nonceBlob.isOk()) {
+ LOG(ERROR) << "GCM encryption but no nonce generated";
+ return false;
+ }
+ // nonceBlob here is just a pointer into existing data, must not be freed
+ std::string nonce(reinterpret_cast<const char*>(&nonceBlob.value()[0]), nonceBlob.value().size());
+ if (!checkSize("nonce", nonce.size(), GCM_NONCE_BYTES)) return false;
+ std::string body;
+ if (!opHandle.updateCompletely(message, &body)) return false;
+
+ std::string mac;
+ if (!opHandle.finish(&mac)) return false;
+ if (!checkSize("mac", mac.size(), GCM_MAC_BYTES)) return false;
+ *ciphertext = nonce + body + mac;
+ return true;
+}*/
+
+static bool decryptWithKeymasterKey(Keymaster& keymaster, const std::string& dir,
+ const AuthorizationSet &keyParams,
+ const std::string& ciphertext, std::string* message) {
+ auto nonce = ciphertext.substr(0, GCM_NONCE_BYTES);
+ auto bodyAndMac = ciphertext.substr(GCM_NONCE_BYTES);
+ auto opParams = AuthorizationSetBuilder()
+ .Authorization(TAG_NONCE, blob2hidlVec(nonce));
+ auto opHandle = begin(keymaster, dir, KeyPurpose::DECRYPT, keyParams, opParams, nullptr);
+ if (!opHandle) return false;
+ if (!opHandle.updateCompletely(bodyAndMac, message)) return false;
+ if (!opHandle.finish(nullptr)) return false;
+ return true;
+}
+
+static std::string getStretching(const KeyAuthentication& auth) {
+ if (!auth.usesKeymaster()) {
+ return kStretch_none;
+ } else if (auth.secret.empty()) {
+ return kStretch_nopassword;
+ } else {
+ char paramstr[PROPERTY_VALUE_MAX];
+
+ property_get(SCRYPT_PROP, paramstr, SCRYPT_DEFAULTS);
+ return std::string() + kStretchPrefix_scrypt + paramstr;
+ }
+}
+
+static bool stretchingNeedsSalt(const std::string& stretching) {
+ return stretching != kStretch_nopassword && stretching != kStretch_none;
+}
+
+static bool stretchSecret(const std::string& stretching, const std::string& secret,
+ const std::string& salt, std::string* stretched) {
+ if (stretching == kStretch_nopassword) {
+ if (!secret.empty()) {
+ LOG(WARNING) << "Password present but stretching is nopassword";
+ // Continue anyway
+ }
+ stretched->clear();
+ } else if (stretching == kStretch_none) {
+ *stretched = secret;
+ } else if (std::equal(kStretchPrefix_scrypt.begin(), kStretchPrefix_scrypt.end(),
+ stretching.begin())) {
+ int Nf, rf, pf;
+ if (!parse_scrypt_parameters(stretching.substr(kStretchPrefix_scrypt.size()).c_str(), &Nf,
+ &rf, &pf)) {
+ LOG(ERROR) << "Unable to parse scrypt params in stretching: " << stretching;
+ return false;
+ }
+ stretched->assign(STRETCHED_BYTES, '\0');
+ if (crypto_scrypt(reinterpret_cast<const uint8_t*>(secret.data()), secret.size(),
+ reinterpret_cast<const uint8_t*>(salt.data()), salt.size(),
+ 1 << Nf, 1 << rf, 1 << pf,
+ reinterpret_cast<uint8_t*>(&(*stretched)[0]), stretched->size()) != 0) {
+ LOG(ERROR) << "scrypt failed with params: " << stretching;
+ return false;
+ }
+ } else {
+ LOG(ERROR) << "Unknown stretching type: " << stretching;
+ return false;
+ }
+ return true;
+}
+
+static bool generateAppId(const KeyAuthentication& auth, const std::string& stretching,
+ const std::string& salt, const std::string& secdiscardable,
+ std::string* appId) {
+ std::string stretched;
+ if (!stretchSecret(stretching, auth.secret, salt, &stretched)) return false;
+ *appId = hashWithPrefix(kHashPrefix_secdiscardable, secdiscardable) + stretched;
+ return true;
+}
+
+static bool readRandomBytesOrLog(size_t count, std::string* out) {
+ auto status = ReadRandomBytes(count, *out);
+ if (status != OK) {
+ LOG(ERROR) << "Random read failed with status: " << status;
+ return false;
+ }
+ return true;
+}
+
+static void logOpensslError() {
+ LOG(ERROR) << "Openssl error: " << ERR_get_error();
+}
+
+static bool encryptWithoutKeymaster(const std::string& preKey,
+ const std::string& plaintext, std::string* ciphertext) {
+ auto key = hashWithPrefix(kHashPrefix_keygen, preKey);
+ key.resize(AES_KEY_BYTES);
+ if (!readRandomBytesOrLog(GCM_NONCE_BYTES, ciphertext)) return false;
+ auto ctx = std::unique_ptr<EVP_CIPHER_CTX, decltype(&::EVP_CIPHER_CTX_free)>(
+ EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free);
+ if (!ctx) {
+ logOpensslError();
+ return false;
+ }
+ if (1 != EVP_EncryptInit_ex(ctx.get(), EVP_aes_256_gcm(), NULL,
+ reinterpret_cast<const uint8_t*>(key.data()),
+ reinterpret_cast<const uint8_t*>(ciphertext->data()))) {
+ logOpensslError();
+ return false;
+ }
+ ciphertext->resize(GCM_NONCE_BYTES + plaintext.size() + GCM_MAC_BYTES);
+ int outlen;
+ if (1 != EVP_EncryptUpdate(ctx.get(),
+ reinterpret_cast<uint8_t*>(&(*ciphertext)[0] + GCM_NONCE_BYTES), &outlen,
+ reinterpret_cast<const uint8_t*>(plaintext.data()), plaintext.size())) {
+ logOpensslError();
+ return false;
+ }
+ if (outlen != static_cast<int>(plaintext.size())) {
+ LOG(ERROR) << "GCM ciphertext length should be " << plaintext.size() << " was " << outlen;
+ return false;
+ }
+ if (1 != EVP_EncryptFinal_ex(ctx.get(),
+ reinterpret_cast<uint8_t*>(&(*ciphertext)[0] + GCM_NONCE_BYTES + plaintext.size()), &outlen)) {
+ logOpensslError();
+ return false;
+ }
+ if (outlen != 0) {
+ LOG(ERROR) << "GCM EncryptFinal should be 0, was " << outlen;
+ return false;
+ }
+ if (1 != EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_GET_TAG, GCM_MAC_BYTES,
+ reinterpret_cast<uint8_t*>(&(*ciphertext)[0] + GCM_NONCE_BYTES + plaintext.size()))) {
+ logOpensslError();
+ return false;
+ }
+ return true;
+}
+
+static bool decryptWithoutKeymaster(const std::string& preKey,
+ const std::string& ciphertext, std::string* plaintext) {
+ if (ciphertext.size() < GCM_NONCE_BYTES + GCM_MAC_BYTES) {
+ LOG(ERROR) << "GCM ciphertext too small: " << ciphertext.size();
+ return false;
+ }
+ auto key = hashWithPrefix(kHashPrefix_keygen, preKey);
+ key.resize(AES_KEY_BYTES);
+ auto ctx = std::unique_ptr<EVP_CIPHER_CTX, decltype(&::EVP_CIPHER_CTX_free)>(
+ EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free);
+ if (!ctx) {
+ logOpensslError();
+ return false;
+ }
+ if (1 != EVP_DecryptInit_ex(ctx.get(), EVP_aes_256_gcm(), NULL,
+ reinterpret_cast<const uint8_t*>(key.data()),
+ reinterpret_cast<const uint8_t*>(ciphertext.data()))) {
+ logOpensslError();
+ return false;
+ }
+ plaintext->resize(ciphertext.size() - GCM_NONCE_BYTES - GCM_MAC_BYTES);
+ int outlen;
+ if (1 != EVP_DecryptUpdate(ctx.get(),
+ reinterpret_cast<uint8_t*>(&(*plaintext)[0]), &outlen,
+ reinterpret_cast<const uint8_t*>(ciphertext.data() + GCM_NONCE_BYTES), plaintext->size())) {
+ logOpensslError();
+ return false;
+ }
+ if (outlen != static_cast<int>(plaintext->size())) {
+ LOG(ERROR) << "GCM plaintext length should be " << plaintext->size() << " was " << outlen;
+ return false;
+ }
+ if (1 != EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_SET_TAG, GCM_MAC_BYTES,
+ const_cast<void *>(
+ reinterpret_cast<const void*>(ciphertext.data() + GCM_NONCE_BYTES + plaintext->size())))) {
+ logOpensslError();
+ return false;
+ }
+ if (1 != EVP_DecryptFinal_ex(ctx.get(),
+ reinterpret_cast<uint8_t*>(&(*plaintext)[0] + plaintext->size()), &outlen)) {
+ logOpensslError();
+ return false;
+ }
+ if (outlen != 0) {
+ LOG(ERROR) << "GCM EncryptFinal should be 0, was " << outlen;
+ return false;
+ }
+ return true;
+}
+
+/*bool storeKey(const std::string& dir, const KeyAuthentication& auth, const std::string& key) {
+ if (TEMP_FAILURE_RETRY(mkdir(dir.c_str(), 0700)) == -1) {
+ PLOG(ERROR) << "key mkdir " << dir;
+ return false;
+ }
+ if (!writeStringToFile(kCurrentVersion, dir + "/" + kFn_version)) return false;
+ std::string secdiscardable;
+ if (!readRandomBytesOrLog(SECDISCARDABLE_BYTES, &secdiscardable)) return false;
+ if (!writeStringToFile(secdiscardable, dir + "/" + kFn_secdiscardable)) return false;
+ std::string stretching = getStretching(auth);
+ if (!writeStringToFile(stretching, dir + "/" + kFn_stretching)) return false;
+ std::string salt;
+ if (stretchingNeedsSalt(stretching)) {
+ if (ReadRandomBytes(SALT_BYTES, salt) != OK) {
+ LOG(ERROR) << "Random read failed";
+ return false;
+ }
+ if (!writeStringToFile(salt, dir + "/" + kFn_salt)) return false;
+ }
+ std::string appId;
+ if (!generateAppId(auth, stretching, salt, secdiscardable, &appId)) return false;
+ std::string encryptedKey;
+ if (auth.usesKeymaster()) {
+ Keymaster keymaster;
+ if (!keymaster) return false;
+ std::string kmKey;
+ if (!generateKeymasterKey(keymaster, auth, appId, &kmKey)) return false;
+ if (!writeStringToFile(kmKey, dir + "/" + kFn_keymaster_key_blob)) return false;
+ auto keyParams = beginParams(auth, appId);
+ if (!encryptWithKeymasterKey(keymaster, dir, keyParams, key, &encryptedKey)) return false;
+ } else {
+ if (!encryptWithoutKeymaster(appId, key, &encryptedKey)) return false;
+ }
+ if (!writeStringToFile(encryptedKey, dir + "/" + kFn_encrypted_key)) return false;
+ return true;
+}*/
+
+bool retrieveKey(const std::string& dir, const KeyAuthentication& auth, std::string* key) {
+ std::string version;
+ if (!readFileToString(dir + "/" + kFn_version, &version)) return false;
+ if (version != kCurrentVersion) {
+ LOG(ERROR) << "Version mismatch, expected " << kCurrentVersion << " got " << version;
+ return false;
+ }
+ std::string secdiscardable;
+ if (!readFileToString(dir + "/" + kFn_secdiscardable, &secdiscardable)) return false;
+ std::string stretching;
+ if (!readFileToString(dir + "/" + kFn_stretching, &stretching)) return false;
+ std::string salt;
+ if (stretchingNeedsSalt(stretching)) {
+ if (!readFileToString(dir + "/" + kFn_salt, &salt)) return false;
+ }
+ std::string appId;
+ if (!generateAppId(auth, stretching, salt, secdiscardable, &appId)) return false;
+ std::string encryptedMessage;
+ if (!readFileToString(dir + "/" + kFn_encrypted_key, &encryptedMessage)) return false;
+ if (auth.usesKeymaster()) {
+ Keymaster keymaster;
+ if (!keymaster) return false;
+ auto keyParams = beginParams(auth, appId);
+ if (!decryptWithKeymasterKey(keymaster, dir, keyParams, encryptedMessage, key)) return false;
+ } else {
+ if (!decryptWithoutKeymaster(appId, encryptedMessage, key)) return false;
+ }
+ return true;
+}
+
+static bool deleteKey(const std::string& dir) {
+ std::string kmKey;
+ if (!readFileToString(dir + "/" + kFn_keymaster_key_blob, &kmKey)) return false;
+ Keymaster keymaster;
+ if (!keymaster) return false;
+ if (!keymaster.deleteKey(kmKey)) return false;
+ return true;
+}
+
+static bool runSecdiscard(const std::string& dir) {
+ if (ForkExecvp(
+ std::vector<std::string>{kSecdiscardPath, "--",
+ dir + "/" + kFn_encrypted_key,
+ dir + "/" + kFn_keymaster_key_blob,
+ dir + "/" + kFn_secdiscardable,
+ }) != 0) {
+ LOG(ERROR) << "secdiscard failed";
+ return false;
+ }
+ return true;
+}
+
+bool runSecdiscardSingle(const std::string& file) {
+ if (ForkExecvp(
+ std::vector<std::string>{kSecdiscardPath, "--",
+ file}) != 0) {
+ LOG(ERROR) << "secdiscard failed";
+ return false;
+ }
+ return true;
+}
+
+static bool recursiveDeleteKey(const std::string& dir) {
+ if (ForkExecvp(std::vector<std::string>{kRmPath, "-rf", dir}) != 0) {
+ LOG(ERROR) << "recursive delete failed";
+ return false;
+ }
+ return true;
+}
+
+bool destroyKey(const std::string& dir) {
+ bool success = true;
+ // Try each thing, even if previous things failed.
+ success &= deleteKey(dir);
+ success &= runSecdiscard(dir);
+ success &= recursiveDeleteKey(dir);
+ return success;
+}
+
+} // namespace vold
+} // namespace android
diff --git a/crypto/ext4crypt/KeyStorage3.h b/crypto/ext4crypt/KeyStorage3.h
new file mode 100644
index 0000000..bce6a99
--- /dev/null
+++ b/crypto/ext4crypt/KeyStorage3.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_VOLD_KEYSTORAGE_H
+#define ANDROID_VOLD_KEYSTORAGE_H
+
+#include <string>
+
+namespace android {
+namespace vold {
+
+// Represents the information needed to decrypt a disk encryption key.
+// If "token" is nonempty, it is passed in as a required Gatekeeper auth token.
+// If "token" and "secret" are nonempty, "secret" is appended to the application-specific
+// binary needed to unlock.
+// If only "secret" is nonempty, it is used to decrypt in a non-Keymaster process.
+class KeyAuthentication {
+ public:
+ KeyAuthentication(std::string t, std::string s) : token{t}, secret{s} {};
+
+ bool usesKeymaster() const { return !token.empty() || secret.empty(); };
+
+ const std::string token;
+ const std::string secret;
+};
+
+extern const KeyAuthentication kEmptyAuthentication;
+
+// Create a directory at the named path, and store "key" in it,
+// in such a way that it can only be retrieved via Keymaster and
+// can be securely deleted.
+// It's safe to move/rename the directory after creation.
+bool storeKey(const std::string& dir, const KeyAuthentication& auth, const std::string& key);
+
+// Retrieve the key from the named directory.
+bool retrieveKey(const std::string& dir, const KeyAuthentication& auth, std::string* key);
+
+// Securely destroy the key stored in the named directory and delete the directory.
+bool destroyKey(const std::string& dir);
+
+bool runSecdiscardSingle(const std::string& file);
+} // namespace vold
+} // namespace android
+
+#endif
diff --git a/crypto/ext4crypt/KeyStorage4.cpp b/crypto/ext4crypt/KeyStorage4.cpp
new file mode 100644
index 0000000..b086791
--- /dev/null
+++ b/crypto/ext4crypt/KeyStorage4.cpp
@@ -0,0 +1,656 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "KeyStorage4.h"
+
+#include "Keymaster4.h"
+#include "ScryptParameters.h"
+#include "Utils.h"
+
+#include <vector>
+
+#include <errno.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/sha.h>
+
+#include <android-base/file.h>
+//#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+
+#include <cutils/properties.h>
+
+#include <hardware/hw_auth_token.h>
+#include <keymasterV4_0/authorization_set.h>
+#include <keymasterV4_0/keymaster_utils.h>
+
+#include <iostream>
+#define ERROR 1
+#define LOG(x) std::cout
+#define PLOG(x) std::cout
+
+extern "C" {
+
+#include "crypto_scrypt.h"
+}
+
+namespace android {
+namespace vold {
+
+const KeyAuthentication kEmptyAuthentication{"", ""};
+
+static constexpr size_t AES_KEY_BYTES = 32;
+static constexpr size_t GCM_NONCE_BYTES = 12;
+static constexpr size_t GCM_MAC_BYTES = 16;
+static constexpr size_t SALT_BYTES = 1 << 4;
+static constexpr size_t SECDISCARDABLE_BYTES = 1 << 14;
+static constexpr size_t STRETCHED_BYTES = 1 << 6;
+
+static constexpr uint32_t AUTH_TIMEOUT = 30; // Seconds
+constexpr int EXT4_AES_256_XTS_KEY_SIZE = 64;
+
+static const char* kCurrentVersion = "1";
+static const char* kRmPath = "/system/bin/rm";
+static const char* kSecdiscardPath = "/system/bin/secdiscard";
+static const char* kStretch_none = "none";
+static const char* kStretch_nopassword = "nopassword";
+static const std::string kStretchPrefix_scrypt = "scrypt ";
+static const char* kHashPrefix_secdiscardable = "Android secdiscardable SHA512";
+static const char* kHashPrefix_keygen = "Android key wrapping key generation SHA512";
+static const char* kFn_encrypted_key = "encrypted_key";
+static const char* kFn_keymaster_key_blob = "keymaster_key_blob";
+static const char* kFn_keymaster_key_blob_upgraded = "keymaster_key_blob_upgraded";
+static const char* kFn_salt = "salt";
+static const char* kFn_secdiscardable = "secdiscardable";
+static const char* kFn_stretching = "stretching";
+static const char* kFn_version = "version";
+
+static bool checkSize(const std::string& kind, size_t actual, size_t expected) {
+ if (actual != expected) {
+ LOG(ERROR) << "Wrong number of bytes in " << kind << ", expected " << expected << " got "
+ << actual << std::endl;
+ return false;
+ }
+ return true;
+}
+
+static void hashWithPrefix(char const* prefix, const std::string& tohash, std::string* res) {
+ SHA512_CTX c;
+
+ SHA512_Init(&c);
+ // Personalise the hashing by introducing a fixed prefix.
+ // Hashing applications should use personalization except when there is a
+ // specific reason not to; see section 4.11 of https://www.schneier.com/skein1.3.pdf
+ std::string hashingPrefix = prefix;
+ hashingPrefix.resize(SHA512_CBLOCK);
+ SHA512_Update(&c, hashingPrefix.data(), hashingPrefix.size());
+ SHA512_Update(&c, tohash.data(), tohash.size());
+ res->assign(SHA512_DIGEST_LENGTH, '\0');
+ SHA512_Final(reinterpret_cast<uint8_t*>(&(*res)[0]), &c);
+}
+
+static bool generateKeymasterKey(Keymaster& keymaster, const KeyAuthentication& auth,
+ const std::string& appId, std::string* key) {
+ auto paramBuilder = km::AuthorizationSetBuilder()
+ .AesEncryptionKey(AES_KEY_BYTES * 8)
+ .GcmModeMinMacLen(GCM_MAC_BYTES * 8)
+ .Authorization(km::TAG_APPLICATION_ID, km::support::blob2hidlVec(appId));
+ if (auth.token.empty()) {
+ LOG(DEBUG) << "Creating key that doesn't need auth token" << std::endl;
+ paramBuilder.Authorization(km::TAG_NO_AUTH_REQUIRED);
+ } else {
+ LOG(DEBUG) << "Auth token required for key" << std::endl;
+ if (auth.token.size() != sizeof(hw_auth_token_t)) {
+ LOG(ERROR) << "Auth token should be " << sizeof(hw_auth_token_t) << " bytes, was "
+ << auth.token.size() << " bytes" << std::endl;
+ return false;
+ }
+ const hw_auth_token_t* at = reinterpret_cast<const hw_auth_token_t*>(auth.token.data());
+ paramBuilder.Authorization(km::TAG_USER_SECURE_ID, at->user_id);
+ paramBuilder.Authorization(km::TAG_USER_AUTH_TYPE, km::HardwareAuthenticatorType::PASSWORD);
+ paramBuilder.Authorization(km::TAG_AUTH_TIMEOUT, AUTH_TIMEOUT);
+ }
+ return keymaster.generateKey(paramBuilder, key);
+}
+
+bool generateWrappedKey(userid_t user_id, KeyType key_type,
+ KeyBuffer* key) {
+ Keymaster keymaster;
+ if (!keymaster) return false;
+ *key = KeyBuffer(EXT4_AES_256_XTS_KEY_SIZE);
+ std::string key_temp;
+ auto paramBuilder = km::AuthorizationSetBuilder()
+ .AesEncryptionKey(AES_KEY_BYTES * 8)
+ .GcmModeMinMacLen(GCM_MAC_BYTES * 8)
+ .Authorization(km::TAG_USER_ID, user_id);
+ km::KeyParameter param1;
+ param1.tag = (km::Tag) (android::hardware::keymaster::V4_0::KM_TAG_FBE_ICE);
+ param1.f.boolValue = true;
+ paramBuilder.push_back(param1);
+ km::KeyParameter param2;
+ if ((key_type == KeyType::DE_USER) || (key_type == KeyType::DE_SYS)) {
+ param2.tag = (km::Tag) (android::hardware::keymaster::V4_0::KM_TAG_KEY_TYPE);
+ param2.f.integer = 0;
+ } else if (key_type == KeyType::CE_USER) {
+ param2.tag = (km::Tag) (android::hardware::keymaster::V4_0::KM_TAG_KEY_TYPE);
+ param2.f.integer = 1;
+ }
+ paramBuilder.push_back(param2);
+ if (!keymaster.generateKey(paramBuilder, &key_temp)) return false;
+ *key = KeyBuffer(key_temp.size());
+ memcpy(reinterpret_cast<void*>(key->data()), key_temp.c_str(), key->size());
+ return true;
+}
+
+bool getEphemeralWrappedKey(km::KeyFormat format, KeyBuffer& kmKey, KeyBuffer* key) {
+ std::string key_temp;
+ Keymaster keymaster;
+ if (!keymaster) return false;
+
+ //Export once, if upgrade needed, upgrade and export again
+ bool export_again = true;
+ while (export_again) {
+ export_again = false;
+ auto ret = keymaster.exportKey(format, kmKey, "!", "!", &key_temp);
+ if (ret == km::ErrorCode::OK) {
+ *key = KeyBuffer(key_temp.size());
+ memcpy(reinterpret_cast<void*>(key->data()), key_temp.c_str(), key->size());
+ return true;
+ }
+ if (ret != km::ErrorCode::KEY_REQUIRES_UPGRADE) return false;
+ LOG(DEBUG) << "Upgrading key" << std::endl;
+ std::string kmKeyStr(reinterpret_cast<const char*>(kmKey.data()), kmKey.size());
+ std::string newKey;
+ if (!keymaster.upgradeKey(kmKeyStr, km::AuthorizationSet(), &newKey)) return false;
+ memcpy(reinterpret_cast<void*>(kmKey.data()), newKey.c_str(), kmKey.size());
+ LOG(INFO) << "Key upgraded" << std::endl;
+ export_again = true;
+ }
+ //Should never come here
+ return false;
+}
+
+static std::pair<km::AuthorizationSet, km::HardwareAuthToken> beginParams(
+ const KeyAuthentication& auth, const std::string& appId) {
+ auto paramBuilder = km::AuthorizationSetBuilder()
+ .GcmModeMacLen(GCM_MAC_BYTES * 8)
+ .Authorization(km::TAG_APPLICATION_ID, km::support::blob2hidlVec(appId));
+ km::HardwareAuthToken authToken;
+ if (!auth.token.empty()) {
+ LOG(DEBUG) << "Supplying auth token to Keymaster" << std::endl;
+ authToken = km::support::hidlVec2AuthToken(km::support::blob2hidlVec(auth.token));
+ }
+ return {paramBuilder, authToken};
+}
+
+static bool readFileToString(const std::string& filename, std::string* result) {
+ if (!android::base::ReadFileToString(filename, result)) {
+ PLOG(ERROR) << "Failed to read from " << filename << std::endl;
+ return false;
+ }
+ return true;
+}
+
+static bool writeStringToFile(const std::string& payload, const std::string& filename) {
+ PLOG(ERROR) << __FUNCTION__ << " called for " << filename << " and being skipped\n";
+ return true;
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(
+ open(filename.c_str(), O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC | O_CLOEXEC, 0666)));
+ if (fd == -1) {
+ PLOG(ERROR) << "Failed to open " << filename;
+ return false;
+ }
+ if (!android::base::WriteStringToFd(payload, fd)) {
+ PLOG(ERROR) << "Failed to write to " << filename;
+ unlink(filename.c_str());
+ return false;
+ }
+ // fsync as close won't guarantee flush data
+ // see close(2), fsync(2) and b/68901441
+ if (fsync(fd) == -1) {
+ if (errno == EROFS || errno == EINVAL) {
+ PLOG(WARNING) << "Skip fsync " << filename
+ << " on a file system does not support synchronization";
+ } else {
+ PLOG(ERROR) << "Failed to fsync " << filename;
+ unlink(filename.c_str());
+ return false;
+ }
+ }
+ return true;
+}
+
+static bool readRandomBytesOrLog(size_t count, std::string* out) {
+ auto status = ReadRandomBytes(count, *out);
+ if (status != OK) {
+ LOG(ERROR) << "Random read failed with status: " << status << std::endl;
+ return false;
+ }
+ return true;
+}
+
+bool createSecdiscardable(const std::string& filename, std::string* hash) {
+ std::string secdiscardable;
+ if (!readRandomBytesOrLog(SECDISCARDABLE_BYTES, &secdiscardable)) return false;
+ if (!writeStringToFile(secdiscardable, filename)) return false;
+ hashWithPrefix(kHashPrefix_secdiscardable, secdiscardable, hash);
+ return true;
+}
+
+bool readSecdiscardable(const std::string& filename, std::string* hash) {
+ std::string secdiscardable;
+ if (!readFileToString(filename, &secdiscardable)) return false;
+ hashWithPrefix(kHashPrefix_secdiscardable, secdiscardable, hash);
+ return true;
+}
+
+static KeymasterOperation begin(Keymaster& keymaster, const std::string& dir,
+ km::KeyPurpose purpose, const km::AuthorizationSet& keyParams,
+ const km::AuthorizationSet& opParams,
+ const km::HardwareAuthToken& authToken,
+ km::AuthorizationSet* outParams) {
+ auto kmKeyPath = dir + "/" + kFn_keymaster_key_blob;
+ std::string kmKey;
+ if (!readFileToString(kmKeyPath, &kmKey)) return KeymasterOperation();
+ km::AuthorizationSet inParams(keyParams);
+ inParams.append(opParams.begin(), opParams.end());
+ for (;;) {
+ auto opHandle = keymaster.begin(purpose, kmKey, inParams, authToken, outParams);
+ if (opHandle) {
+ return opHandle;
+ }
+ if (opHandle.errorCode() != km::ErrorCode::KEY_REQUIRES_UPGRADE) return opHandle;
+ LOG(DEBUG) << "Upgrading key in memory only: " << dir << std::endl;
+ std::string newKey;
+ if (!keymaster.upgradeKey(kmKey, keyParams, &newKey)) return KeymasterOperation();
+ /*auto newKeyPath = dir + "/" + kFn_keymaster_key_blob_upgraded;
+ if (!writeStringToFile(newKey, newKeyPath)) return KeymasterOperation();
+ if (rename(newKeyPath.c_str(), kmKeyPath.c_str()) != 0) {
+ PLOG(ERROR) << "Unable to move upgraded key to location: " << kmKeyPath;
+ return KeymasterOperation();
+ }
+ if (!keymaster.deleteKey(kmKey)) {
+ LOG(ERROR) << "Key deletion failed during upgrade, continuing anyway: " << dir;
+ }*/
+ kmKey = newKey;
+ LOG(INFO) << "Key upgraded in memory but not updated in folder: " << dir << std::endl;
+ }
+}
+
+static bool encryptWithKeymasterKey(Keymaster& keymaster, const std::string& dir,
+ const km::AuthorizationSet& keyParams,
+ const km::HardwareAuthToken& authToken,
+ const KeyBuffer& message, std::string* ciphertext) {
+ km::AuthorizationSet opParams;
+ km::AuthorizationSet outParams;
+ auto opHandle =
+ begin(keymaster, dir, km::KeyPurpose::ENCRYPT, keyParams, opParams, authToken, &outParams);
+ if (!opHandle) return false;
+ auto nonceBlob = outParams.GetTagValue(km::TAG_NONCE);
+ if (!nonceBlob.isOk()) {
+ LOG(ERROR) << "GCM encryption but no nonce generated" << std::endl;
+ return false;
+ }
+ // nonceBlob here is just a pointer into existing data, must not be freed
+ std::string nonce(reinterpret_cast<const char*>(&nonceBlob.value()[0]),
+ nonceBlob.value().size());
+ if (!checkSize("nonce", nonce.size(), GCM_NONCE_BYTES)) return false;
+ std::string body;
+ if (!opHandle.updateCompletely(message, &body)) return false;
+
+ std::string mac;
+ if (!opHandle.finish(&mac)) return false;
+ if (!checkSize("mac", mac.size(), GCM_MAC_BYTES)) return false;
+ *ciphertext = nonce + body + mac;
+ return true;
+}
+
+static bool decryptWithKeymasterKey(Keymaster& keymaster, const std::string& dir,
+ const km::AuthorizationSet& keyParams,
+ const km::HardwareAuthToken& authToken,
+ const std::string& ciphertext, KeyBuffer* message) {
+ auto nonce = ciphertext.substr(0, GCM_NONCE_BYTES);
+ auto bodyAndMac = ciphertext.substr(GCM_NONCE_BYTES);
+ auto opParams = km::AuthorizationSetBuilder().Authorization(km::TAG_NONCE,
+ km::support::blob2hidlVec(nonce));
+ auto opHandle =
+ begin(keymaster, dir, km::KeyPurpose::DECRYPT, keyParams, opParams, authToken, nullptr);
+ if (!opHandle) return false;
+ if (!opHandle.updateCompletely(bodyAndMac, message)) return false;
+ if (!opHandle.finish(nullptr)) return false;
+ return true;
+}
+
+static std::string getStretching(const KeyAuthentication& auth) {
+ if (!auth.usesKeymaster()) {
+ return kStretch_none;
+ } else if (auth.secret.empty()) {
+ return kStretch_nopassword;
+ } else {
+ char paramstr[PROPERTY_VALUE_MAX];
+
+ property_get(SCRYPT_PROP, paramstr, SCRYPT_DEFAULTS);
+ return std::string() + kStretchPrefix_scrypt + paramstr;
+ }
+}
+
+static bool stretchingNeedsSalt(const std::string& stretching) {
+ return stretching != kStretch_nopassword && stretching != kStretch_none;
+}
+
+static bool stretchSecret(const std::string& stretching, const std::string& secret,
+ const std::string& salt, std::string* stretched) {
+ if (stretching == kStretch_nopassword) {
+ if (!secret.empty()) {
+ LOG(WARNING) << "Password present but stretching is nopassword" << std::endl;
+ // Continue anyway
+ }
+ stretched->clear();
+ } else if (stretching == kStretch_none) {
+ *stretched = secret;
+ } else if (std::equal(kStretchPrefix_scrypt.begin(), kStretchPrefix_scrypt.end(),
+ stretching.begin())) {
+ int Nf, rf, pf;
+ if (!parse_scrypt_parameters(stretching.substr(kStretchPrefix_scrypt.size()).c_str(), &Nf,
+ &rf, &pf)) {
+ LOG(ERROR) << "Unable to parse scrypt params in stretching: " << stretching << std::endl;
+ return false;
+ }
+ stretched->assign(STRETCHED_BYTES, '\0');
+ if (crypto_scrypt(reinterpret_cast<const uint8_t*>(secret.data()), secret.size(),
+ reinterpret_cast<const uint8_t*>(salt.data()), salt.size(), 1 << Nf,
+ 1 << rf, 1 << pf, reinterpret_cast<uint8_t*>(&(*stretched)[0]),
+ stretched->size()) != 0) {
+ LOG(ERROR) << "scrypt failed with params: " << stretching << std::endl;
+ return false;
+ }
+ } else {
+ LOG(ERROR) << "Unknown stretching type: " << stretching << std::endl;
+ return false;
+ }
+ return true;
+}
+
+static bool generateAppId(const KeyAuthentication& auth, const std::string& stretching,
+ const std::string& salt, const std::string& secdiscardable_hash,
+ std::string* appId) {
+ std::string stretched;
+ if (!stretchSecret(stretching, auth.secret, salt, &stretched)) return false;
+ *appId = secdiscardable_hash + stretched;
+ return true;
+}
+
+static void logOpensslError() {
+ LOG(ERROR) << "Openssl error: " << ERR_get_error() << std::endl;
+}
+
+static bool encryptWithoutKeymaster(const std::string& preKey, const KeyBuffer& plaintext,
+ std::string* ciphertext) {
+ std::string key;
+ hashWithPrefix(kHashPrefix_keygen, preKey, &key);
+ key.resize(AES_KEY_BYTES);
+ if (!readRandomBytesOrLog(GCM_NONCE_BYTES, ciphertext)) return false;
+ auto ctx = std::unique_ptr<EVP_CIPHER_CTX, decltype(&::EVP_CIPHER_CTX_free)>(
+ EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free);
+ if (!ctx) {
+ logOpensslError();
+ return false;
+ }
+ if (1 != EVP_EncryptInit_ex(ctx.get(), EVP_aes_256_gcm(), NULL,
+ reinterpret_cast<const uint8_t*>(key.data()),
+ reinterpret_cast<const uint8_t*>(ciphertext->data()))) {
+ logOpensslError();
+ return false;
+ }
+ ciphertext->resize(GCM_NONCE_BYTES + plaintext.size() + GCM_MAC_BYTES);
+ int outlen;
+ if (1 != EVP_EncryptUpdate(
+ ctx.get(), reinterpret_cast<uint8_t*>(&(*ciphertext)[0] + GCM_NONCE_BYTES),
+ &outlen, reinterpret_cast<const uint8_t*>(plaintext.data()), plaintext.size())) {
+ logOpensslError();
+ return false;
+ }
+ if (outlen != static_cast<int>(plaintext.size())) {
+ LOG(ERROR) << "GCM ciphertext length should be " << plaintext.size() << " was " << outlen << std::endl;
+ return false;
+ }
+ if (1 != EVP_EncryptFinal_ex(
+ ctx.get(),
+ reinterpret_cast<uint8_t*>(&(*ciphertext)[0] + GCM_NONCE_BYTES + plaintext.size()),
+ &outlen)) {
+ logOpensslError();
+ return false;
+ }
+ if (outlen != 0) {
+ LOG(ERROR) << "GCM EncryptFinal should be 0, was " << outlen << std::endl;
+ return false;
+ }
+ if (1 != EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_GET_TAG, GCM_MAC_BYTES,
+ reinterpret_cast<uint8_t*>(&(*ciphertext)[0] + GCM_NONCE_BYTES +
+ plaintext.size()))) {
+ logOpensslError();
+ return false;
+ }
+ return true;
+}
+
+static bool decryptWithoutKeymaster(const std::string& preKey, const std::string& ciphertext,
+ KeyBuffer* plaintext) {
+ if (ciphertext.size() < GCM_NONCE_BYTES + GCM_MAC_BYTES) {
+ LOG(ERROR) << "GCM ciphertext too small: " << ciphertext.size() << std::endl;
+ return false;
+ }
+ std::string key;
+ hashWithPrefix(kHashPrefix_keygen, preKey, &key);
+ key.resize(AES_KEY_BYTES);
+ auto ctx = std::unique_ptr<EVP_CIPHER_CTX, decltype(&::EVP_CIPHER_CTX_free)>(
+ EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free);
+ if (!ctx) {
+ logOpensslError();
+ return false;
+ }
+ if (1 != EVP_DecryptInit_ex(ctx.get(), EVP_aes_256_gcm(), NULL,
+ reinterpret_cast<const uint8_t*>(key.data()),
+ reinterpret_cast<const uint8_t*>(ciphertext.data()))) {
+ logOpensslError();
+ return false;
+ }
+ *plaintext = KeyBuffer(ciphertext.size() - GCM_NONCE_BYTES - GCM_MAC_BYTES);
+ int outlen;
+ if (1 != EVP_DecryptUpdate(ctx.get(), reinterpret_cast<uint8_t*>(&(*plaintext)[0]), &outlen,
+ reinterpret_cast<const uint8_t*>(ciphertext.data() + GCM_NONCE_BYTES),
+ plaintext->size())) {
+ logOpensslError();
+ return false;
+ }
+ if (outlen != static_cast<int>(plaintext->size())) {
+ LOG(ERROR) << "GCM plaintext length should be " << plaintext->size() << " was " << outlen << std::endl;
+ return false;
+ }
+ if (1 != EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_SET_TAG, GCM_MAC_BYTES,
+ const_cast<void*>(reinterpret_cast<const void*>(
+ ciphertext.data() + GCM_NONCE_BYTES + plaintext->size())))) {
+ logOpensslError();
+ return false;
+ }
+ if (1 != EVP_DecryptFinal_ex(ctx.get(),
+ reinterpret_cast<uint8_t*>(&(*plaintext)[0] + plaintext->size()),
+ &outlen)) {
+ logOpensslError();
+ return false;
+ }
+ if (outlen != 0) {
+ LOG(ERROR) << "GCM EncryptFinal should be 0, was " << outlen << std::endl;
+ return false;
+ }
+ return true;
+}
+
+bool pathExists(const std::string& path) {
+ return access(path.c_str(), F_OK) == 0;
+}
+
+bool storeKey(const std::string& dir, const KeyAuthentication& auth, const KeyBuffer& key) {
+ if (TEMP_FAILURE_RETRY(mkdir(dir.c_str(), 0700)) == -1) {
+ PLOG(ERROR) << "key mkdir " << dir << std::endl;
+ return false;
+ }
+ if (!writeStringToFile(kCurrentVersion, dir + "/" + kFn_version)) return false;
+ std::string secdiscardable_hash;
+ if (!createSecdiscardable(dir + "/" + kFn_secdiscardable, &secdiscardable_hash)) return false;
+ std::string stretching = getStretching(auth);
+ if (!writeStringToFile(stretching, dir + "/" + kFn_stretching)) return false;
+ std::string salt;
+ if (stretchingNeedsSalt(stretching)) {
+ if (ReadRandomBytes(SALT_BYTES, salt) != OK) {
+ LOG(ERROR) << "Random read failed" << std::endl;
+ return false;
+ }
+ if (!writeStringToFile(salt, dir + "/" + kFn_salt)) return false;
+ }
+ std::string appId;
+ if (!generateAppId(auth, stretching, salt, secdiscardable_hash, &appId)) return false;
+ std::string encryptedKey;
+ if (auth.usesKeymaster()) {
+ Keymaster keymaster;
+ if (!keymaster) return false;
+ std::string kmKey;
+ if (!generateKeymasterKey(keymaster, auth, appId, &kmKey)) return false;
+ if (!writeStringToFile(kmKey, dir + "/" + kFn_keymaster_key_blob)) return false;
+ km::AuthorizationSet keyParams;
+ km::HardwareAuthToken authToken;
+ std::tie(keyParams, authToken) = beginParams(auth, appId);
+ if (!encryptWithKeymasterKey(keymaster, dir, keyParams, authToken, key, &encryptedKey))
+ return false;
+ } else {
+ if (!encryptWithoutKeymaster(appId, key, &encryptedKey)) return false;
+ }
+ if (!writeStringToFile(encryptedKey, dir + "/" + kFn_encrypted_key)) return false;
+ return true;
+}
+
+bool storeKeyAtomically(const std::string& key_path, const std::string& tmp_path,
+ const KeyAuthentication& auth, const KeyBuffer& key) {
+ if (pathExists(key_path)) {
+ LOG(ERROR) << "Already exists, cannot create key at: " << key_path << std::endl;
+ return false;
+ }
+ if (pathExists(tmp_path)) {
+ LOG(DEBUG) << "Already exists, destroying: " << tmp_path << std::endl;
+ destroyKey(tmp_path); // May be partially created so ignore errors
+ }
+ if (!storeKey(tmp_path, auth, key)) return false;
+ if (rename(tmp_path.c_str(), key_path.c_str()) != 0) {
+ PLOG(ERROR) << "Unable to move new key to location: " << key_path << std::endl;
+ return false;
+ }
+ LOG(DEBUG) << "Created key: " << key_path << std::endl;
+ return true;
+}
+
+bool retrieveKey(const std::string& dir, const KeyAuthentication& auth, KeyBuffer* key) {
+ std::string version;
+ if (!readFileToString(dir + "/" + kFn_version, &version)) return false;
+ if (version != kCurrentVersion) {
+ LOG(ERROR) << "Version mismatch, expected " << kCurrentVersion << " got " << version << std::endl;
+ return false;
+ }
+ std::string secdiscardable_hash;
+ if (!readSecdiscardable(dir + "/" + kFn_secdiscardable, &secdiscardable_hash)) return false;
+ std::string stretching;
+ if (!readFileToString(dir + "/" + kFn_stretching, &stretching)) return false;
+ std::string salt;
+ if (stretchingNeedsSalt(stretching)) {
+ if (!readFileToString(dir + "/" + kFn_salt, &salt)) return false;
+ }
+ std::string appId;
+ if (!generateAppId(auth, stretching, salt, secdiscardable_hash, &appId)) return false;
+ std::string encryptedMessage;
+ if (!readFileToString(dir + "/" + kFn_encrypted_key, &encryptedMessage)) return false;
+ if (auth.usesKeymaster()) {
+ Keymaster keymaster;
+ if (!keymaster) return false;
+ km::AuthorizationSet keyParams;
+ km::HardwareAuthToken authToken;
+ std::tie(keyParams, authToken) = beginParams(auth, appId);
+ if (!decryptWithKeymasterKey(keymaster, dir, keyParams, authToken, encryptedMessage, key))
+ return false;
+ } else {
+ if (!decryptWithoutKeymaster(appId, encryptedMessage, key)) return false;
+ }
+ return true;
+}
+
+static bool deleteKey(const std::string& dir) {
+ LOG(DEBUG) << "not deleting key in " << __FILE__ << std::endl;
+ return true;
+ std::string kmKey;
+ if (!readFileToString(dir + "/" + kFn_keymaster_key_blob, &kmKey)) return false;
+ Keymaster keymaster;
+ if (!keymaster) return false;
+ if (!keymaster.deleteKey(kmKey)) return false;
+ return true;
+}
+
+bool runSecdiscardSingle(const std::string& file) {
+ if (ForkExecvp(std::vector<std::string>{kSecdiscardPath, "--", file}) != 0) {
+ LOG(ERROR) << "secdiscard failed" << std::endl;
+ return false;
+ }
+ return true;
+}
+
+static bool recursiveDeleteKey(const std::string& dir) {
+ LOG(DEBUG) << "not recursively deleting key in " << __FILE__ << std::endl;
+ return true;
+ if (ForkExecvp(std::vector<std::string>{kRmPath, "-rf", dir}) != 0) {
+ LOG(ERROR) << "recursive delete failed" << std::endl;
+ return false;
+ }
+ return true;
+}
+
+bool destroyKey(const std::string& dir) {
+ LOG(DEBUG) << "not destroying key in " << __FILE__ << std::endl;
+ return true;
+ bool success = true;
+ // Try each thing, even if previous things failed.
+ bool uses_km = pathExists(dir + "/" + kFn_keymaster_key_blob);
+ if (uses_km) {
+ success &= deleteKey(dir);
+ }
+ auto secdiscard_cmd = std::vector<std::string>{
+ kSecdiscardPath, "--", dir + "/" + kFn_encrypted_key, dir + "/" + kFn_secdiscardable,
+ };
+ if (uses_km) {
+ secdiscard_cmd.emplace_back(dir + "/" + kFn_keymaster_key_blob);
+ }
+ if (ForkExecvp(secdiscard_cmd) != 0) {
+ LOG(ERROR) << "secdiscard failed" << std::endl;
+ success = false;
+ }
+ success &= recursiveDeleteKey(dir);
+ return success;
+}
+
+} // namespace vold
+} // namespace android
diff --git a/crypto/ext4crypt/KeyStorage4.h b/crypto/ext4crypt/KeyStorage4.h
new file mode 100644
index 0000000..f74865f
--- /dev/null
+++ b/crypto/ext4crypt/KeyStorage4.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_TWRP_KEYSTORAGE_H
+#define ANDROID_TWRP_KEYSTORAGE_H
+
+#include "Keymaster4.h"
+#include "KeyBuffer.h"
+#include <ext4_utils/ext4_crypt.h>
+
+#include <string>
+
+namespace android {
+namespace vold {
+
+namespace km = ::android::hardware::keymaster::V4_0;
+
+// Represents the information needed to decrypt a disk encryption key.
+// If "token" is nonempty, it is passed in as a required Gatekeeper auth token.
+// If "token" and "secret" are nonempty, "secret" is appended to the application-specific
+// binary needed to unlock.
+// If only "secret" is nonempty, it is used to decrypt in a non-Keymaster process.
+class KeyAuthentication {
+ public:
+ KeyAuthentication(std::string t, std::string s) : token{t}, secret{s} {};
+
+ bool usesKeymaster() const { return !token.empty() || secret.empty(); };
+
+ const std::string token;
+ const std::string secret;
+};
+
+enum class KeyType {
+ DE_SYS,
+ DE_USER,
+ CE_USER
+};
+
+extern const KeyAuthentication kEmptyAuthentication;
+
+// Checks if path "path" exists.
+bool pathExists(const std::string& path);
+
+bool createSecdiscardable(const std::string& path, std::string* hash);
+bool readSecdiscardable(const std::string& path, std::string* hash);
+
+// Create a directory at the named path, and store "key" in it,
+// in such a way that it can only be retrieved via Keymaster and
+// can be securely deleted.
+// It's safe to move/rename the directory after creation.
+bool storeKey(const std::string& dir, const KeyAuthentication& auth, const KeyBuffer& key);
+
+// Create a directory at the named path, and store "key" in it as storeKey
+// This version creates the key in "tmp_path" then atomically renames "tmp_path"
+// to "key_path" thereby ensuring that the key is either stored entirely or
+// not at all.
+bool storeKeyAtomically(const std::string& key_path, const std::string& tmp_path,
+ const KeyAuthentication& auth, const KeyBuffer& key);
+
+// Retrieve the key from the named directory.
+bool retrieveKey(const std::string& dir, const KeyAuthentication& auth, KeyBuffer* key);
+
+// Securely destroy the key stored in the named directory and delete the directory.
+bool destroyKey(const std::string& dir);
+
+bool runSecdiscardSingle(const std::string& file);
+
+bool generateWrappedKey(userid_t user_id, KeyType key_type, KeyBuffer* key);
+bool getEphemeralWrappedKey(km::KeyFormat format, KeyBuffer& kmKey, KeyBuffer* key);
+} // namespace vold
+} // namespace android
+
+#endif
diff --git a/crypto/ext4crypt/KeyUtil.cpp b/crypto/ext4crypt/KeyUtil.cpp
new file mode 100644
index 0000000..3dc0500
--- /dev/null
+++ b/crypto/ext4crypt/KeyUtil.cpp
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "KeyUtil.h"
+
+#include <iomanip>
+#include <sstream>
+#include <string>
+
+#include <openssl/sha.h>
+
+#include <android-base/file.h>
+//#include <android-base/logging.h>
+#include <keyutils.h>
+
+#include "KeyStorage4.h"
+#include "Ext4CryptPie.h"
+#include "Utils.h"
+
+#include <iostream>
+#define LOG(x) std::cout
+#define PLOG(x) std::cout
+#include <sys/types.h>
+#include <unistd.h>
+
+#define MAX_USER_ID 0xFFFFFFFF
+
+using android::hardware::keymaster::V4_0::KeyFormat;
+using android::vold::KeyType;
+
+namespace android {
+namespace vold {
+
+// ext4enc:TODO get this const from somewhere good
+const int EXT4_KEY_DESCRIPTOR_SIZE = 8;
+
+// ext4enc:TODO Include structure from somewhere sensible
+// MUST be in sync with ext4_crypto.c in kernel
+constexpr int EXT4_ENCRYPTION_MODE_AES_256_XTS = 1;
+constexpr int EXT4_AES_256_XTS_KEY_SIZE = 64;
+constexpr int EXT4_MAX_KEY_SIZE = 64;
+struct ext4_encryption_key {
+ uint32_t mode;
+ char raw[EXT4_MAX_KEY_SIZE];
+ uint32_t size;
+};
+
+bool randomKey(KeyBuffer* key) {
+ *key = KeyBuffer(EXT4_AES_256_XTS_KEY_SIZE);
+ if (ReadRandomBytes(key->size(), key->data()) != 0) {
+ // TODO status_t plays badly with PLOG, fix it.
+ LOG(ERROR) << "Random read failed" << std::endl;
+ return false;
+ }
+ return true;
+}
+
+// Get raw keyref - used to make keyname and to pass to ioctl
+static std::string generateKeyRef(const char* key, int length) {
+ SHA512_CTX c;
+
+ SHA512_Init(&c);
+ SHA512_Update(&c, key, length);
+ unsigned char key_ref1[SHA512_DIGEST_LENGTH];
+ SHA512_Final(key_ref1, &c);
+
+ SHA512_Init(&c);
+ SHA512_Update(&c, key_ref1, SHA512_DIGEST_LENGTH);
+ unsigned char key_ref2[SHA512_DIGEST_LENGTH];
+ SHA512_Final(key_ref2, &c);
+
+ static_assert(EXT4_KEY_DESCRIPTOR_SIZE <= SHA512_DIGEST_LENGTH,
+ "Hash too short for descriptor");
+ return std::string((char*)key_ref2, EXT4_KEY_DESCRIPTOR_SIZE);
+}
+
+static bool fillKey(const KeyBuffer& key, ext4_encryption_key* ext4_key) {
+ if (key.size() != EXT4_AES_256_XTS_KEY_SIZE) {
+ LOG(ERROR) << "Wrong size key " << key.size();
+ return false;
+ }
+ static_assert(EXT4_AES_256_XTS_KEY_SIZE <= sizeof(ext4_key->raw), "Key too long!");
+ ext4_key->mode = EXT4_ENCRYPTION_MODE_AES_256_XTS;
+ ext4_key->size = key.size();
+ memset(ext4_key->raw, 0, sizeof(ext4_key->raw));
+ memcpy(ext4_key->raw, key.data(), key.size());
+ return true;
+}
+
+static char const* const NAME_PREFIXES[] = {
+ "ext4",
+ "f2fs",
+ "fscrypt",
+ nullptr
+};
+
+static std::string keyname(const std::string& prefix, const std::string& raw_ref) {
+ std::ostringstream o;
+ o << prefix << ":";
+ for (unsigned char i : raw_ref) {
+ o << std::hex << std::setw(2) << std::setfill('0') << (int)i;
+ }
+ return o.str();
+}
+
+// Get the keyring we store all keys in
+static bool e4cryptKeyring(key_serial_t* device_keyring) {
+ *device_keyring = keyctl_search(KEY_SPEC_SESSION_KEYRING, "keyring", "e4crypt", 0);
+ if (*device_keyring == -1) {
+ PLOG(ERROR) << "Unable to find device keyring" << std::endl;
+ return false;
+ }
+ return true;
+}
+
+// Install password into global keyring
+// Return raw key reference for use in policy
+bool installKey(const KeyBuffer& key, std::string* raw_ref) {
+ // Place ext4_encryption_key into automatically zeroing buffer.
+ KeyBuffer ext4KeyBuffer(sizeof(ext4_encryption_key));
+ ext4_encryption_key &ext4_key = *reinterpret_cast<ext4_encryption_key*>(ext4KeyBuffer.data());
+
+ if (!fillKey(key, &ext4_key)) return false;
+ if (is_wrapped_key_supported()) {
+ /* When wrapped key is supported, only the first 32 bytes are
+ the same per boot. The second 32 bytes can change as the ephemeral
+ key is different. */
+ *raw_ref = generateKeyRef(ext4_key.raw, (ext4_key.size)/2);
+ } else {
+ *raw_ref = generateKeyRef(ext4_key.raw, ext4_key.size);
+ }
+ key_serial_t device_keyring;
+ if (!e4cryptKeyring(&device_keyring)) return false;
+ for (char const* const* name_prefix = NAME_PREFIXES; *name_prefix != nullptr; name_prefix++) {
+ auto ref = keyname(*name_prefix, *raw_ref);
+ key_serial_t key_id =
+ add_key("logon", ref.c_str(), (void*)&ext4_key, sizeof(ext4_key), device_keyring);
+ if (key_id == -1) {
+ PLOG(ERROR) << "Failed to insert key into keyring " << device_keyring << std::endl;
+ return false;
+ }
+ LOG(DEBUG) << "Added key " << key_id << " (" << ref << ") to keyring " << device_keyring
+ << " in process " << getpid() << std::endl;
+ }
+ return true;
+}
+
+bool evictKey(const std::string& raw_ref) {
+ LOG(ERROR) << "not actually evicting key\n";
+ return true;
+ key_serial_t device_keyring;
+ if (!e4cryptKeyring(&device_keyring)) return false;
+ bool success = true;
+ for (char const* const* name_prefix = NAME_PREFIXES; *name_prefix != nullptr; name_prefix++) {
+ auto ref = keyname(*name_prefix, raw_ref);
+ auto key_serial = keyctl_search(device_keyring, "logon", ref.c_str(), 0);
+
+ // Unlink the key from the keyring. Prefer unlinking to revoking or
+ // invalidating, since unlinking is actually no less secure currently, and
+ // it avoids bugs in certain kernel versions where the keyring key is
+ // referenced from places it shouldn't be.
+ if (keyctl_unlink(key_serial, device_keyring) != 0) {
+ PLOG(ERROR) << "Failed to unlink key with serial " << key_serial << " ref " << ref;
+ success = false;
+ } else {
+ LOG(DEBUG) << "Unlinked key with serial " << key_serial << " ref " << ref;
+ }
+ }
+ return success;
+}
+
+bool retrieveAndInstallKey(bool create_if_absent, const KeyAuthentication& key_authentication,
+ const std::string& key_path, const std::string& tmp_path,
+ std::string* key_ref, bool wrapped_key_supported) {
+ KeyBuffer key;
+ if (pathExists(key_path)) {
+ LOG(DEBUG) << "Key exists, using: " << key_path << std::endl;
+ if (!retrieveKey(key_path, key_authentication, &key)) return false;
+ } else {
+ if (!create_if_absent) {
+ LOG(ERROR) << "No key found in " << key_path << std::endl;
+ return false;
+ }
+ LOG(INFO) << "Creating new key in " << key_path << std::endl;
+ if (wrapped_key_supported) {
+ if(!generateWrappedKey(MAX_USER_ID, KeyType::DE_SYS, &key)) return false;
+ } else {
+ if (!randomKey(&key)) return false;
+ }
+ if (!storeKeyAtomically(key_path, tmp_path, key_authentication, key)) return false;
+ }
+
+ if (wrapped_key_supported) {
+ KeyBuffer ephemeral_wrapped_key;
+ if (!getEphemeralWrappedKey(KeyFormat::RAW, key, &ephemeral_wrapped_key)) {
+ LOG(ERROR) << "Failed to export key in retrieveAndInstallKey";
+ return false;
+ }
+ key = std::move(ephemeral_wrapped_key);
+ }
+
+ if (!installKey(key, key_ref)) {
+ LOG(ERROR) << "Failed to install key in " << key_path << std::endl;
+ return false;
+ }
+ return true;
+}
+
+bool retrieveKey(bool create_if_absent, const std::string& key_path,
+ const std::string& tmp_path, KeyBuffer* key) {
+ if (pathExists(key_path)) {
+ LOG(DEBUG) << "Key exists, using: " << key_path << std::endl;
+ if (!retrieveKey(key_path, kEmptyAuthentication, key)) return false;
+ } else {
+ if (!create_if_absent) {
+ LOG(ERROR) << "No key found in " << key_path << std::endl;
+ return false;
+ }
+ LOG(INFO) << "Creating new key in " << key_path << std::endl;
+ if (!randomKey(key)) return false;
+ if (!storeKeyAtomically(key_path, tmp_path,
+ kEmptyAuthentication, *key)) return false;
+ }
+ return true;
+}
+
+} // namespace vold
+} // namespace android
diff --git a/crypto/ext4crypt/KeyUtil.h b/crypto/ext4crypt/KeyUtil.h
new file mode 100644
index 0000000..46a3124
--- /dev/null
+++ b/crypto/ext4crypt/KeyUtil.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_VOLD_KEYUTIL_H
+#define ANDROID_VOLD_KEYUTIL_H
+
+#include "KeyBuffer.h"
+#include "KeyStorage4.h"
+#include "Keymaster4.h"
+
+#include <string>
+#include <memory>
+
+namespace android {
+namespace vold {
+
+bool randomKey(KeyBuffer* key);
+bool installKey(const KeyBuffer& key, std::string* raw_ref);
+bool evictKey(const std::string& raw_ref);
+bool retrieveAndInstallKey(bool create_if_absent, const KeyAuthentication& key_authentication,
+ const std::string& key_path, const std::string& tmp_path,
+ std::string* key_ref, bool wrapped_key_supported);
+bool retrieveKey(bool create_if_absent, const std::string& key_path,
+ const std::string& tmp_path, KeyBuffer* key);
+
+} // namespace vold
+} // namespace android
+
+#endif
diff --git a/crypto/ext4crypt/Keymaster.cpp b/crypto/ext4crypt/Keymaster.cpp
new file mode 100644
index 0000000..3c21aa2
--- /dev/null
+++ b/crypto/ext4crypt/Keymaster.cpp
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Keymaster.h"
+
+//#include <android-base/logging.h>
+#include <hardware/hardware.h>
+#include <hardware/keymaster1.h>
+#include <hardware/keymaster2.h>
+
+#include <iostream>
+#define ERROR 1
+#define LOG(x) std::cout
+
+namespace android {
+namespace vold {
+
+class IKeymasterDevice {
+ public:
+ IKeymasterDevice() {}
+ virtual ~IKeymasterDevice() {}
+ /*virtual keymaster_error_t generate_key(const keymaster_key_param_set_t* params,
+ keymaster_key_blob_t* key_blob) const = 0;*/
+ virtual keymaster_error_t delete_key(const keymaster_key_blob_t* key) const = 0;
+ virtual keymaster_error_t begin(keymaster_purpose_t purpose, const keymaster_key_blob_t* key,
+ const keymaster_key_param_set_t* in_params,
+ keymaster_key_param_set_t* out_params,
+ keymaster_operation_handle_t* operation_handle) const = 0;
+ virtual keymaster_error_t update(keymaster_operation_handle_t operation_handle,
+ const keymaster_key_param_set_t* in_params,
+ const keymaster_blob_t* input, size_t* input_consumed,
+ keymaster_key_param_set_t* out_params,
+ keymaster_blob_t* output) const = 0;
+ virtual keymaster_error_t finish(keymaster_operation_handle_t operation_handle,
+ const keymaster_key_param_set_t* in_params,
+ const keymaster_blob_t* signature,
+ keymaster_key_param_set_t* out_params,
+ keymaster_blob_t* output) const = 0;
+ virtual keymaster_error_t abort(keymaster_operation_handle_t operation_handle) const = 0;
+
+ protected:
+ DISALLOW_COPY_AND_ASSIGN(IKeymasterDevice);
+};
+
+template <typename T> class KeymasterDevice : public IKeymasterDevice {
+ public:
+ KeymasterDevice(T* d) : mDevice{d} {}
+ /*keymaster_error_t generate_key(const keymaster_key_param_set_t* params,
+ keymaster_key_blob_t* key_blob) const override final {
+ return mDevice->generate_key(mDevice, params, key_blob, nullptr);
+ }*/
+ keymaster_error_t delete_key(const keymaster_key_blob_t* key) const override final {
+ if (mDevice->delete_key == nullptr) return KM_ERROR_OK;
+ return mDevice->delete_key(mDevice, key);
+ }
+ keymaster_error_t begin(keymaster_purpose_t purpose, const keymaster_key_blob_t* key,
+ const keymaster_key_param_set_t* in_params,
+ keymaster_key_param_set_t* out_params,
+ keymaster_operation_handle_t* operation_handle) const override final {
+ return mDevice->begin(mDevice, purpose, key, in_params, out_params, operation_handle);
+ }
+ keymaster_error_t update(keymaster_operation_handle_t operation_handle,
+ const keymaster_key_param_set_t* in_params,
+ const keymaster_blob_t* input, size_t* input_consumed,
+ keymaster_key_param_set_t* out_params,
+ keymaster_blob_t* output) const override final {
+ return mDevice->update(mDevice, operation_handle, in_params, input, input_consumed,
+ out_params, output);
+ }
+ keymaster_error_t abort(keymaster_operation_handle_t operation_handle) const override final {
+ return mDevice->abort(mDevice, operation_handle);
+ }
+
+ protected:
+ T* const mDevice;
+};
+
+class Keymaster1Device : public KeymasterDevice<keymaster1_device_t> {
+ public:
+ Keymaster1Device(keymaster1_device_t* d) : KeymasterDevice<keymaster1_device_t>{d} {}
+ ~Keymaster1Device() override final { keymaster1_close(mDevice); }
+ keymaster_error_t finish(keymaster_operation_handle_t operation_handle,
+ const keymaster_key_param_set_t* in_params,
+ const keymaster_blob_t* signature,
+ keymaster_key_param_set_t* out_params,
+ keymaster_blob_t* output) const override final {
+ return mDevice->finish(mDevice, operation_handle, in_params, signature, out_params, output);
+ }
+};
+
+class Keymaster2Device : public KeymasterDevice<keymaster2_device_t> {
+ public:
+ Keymaster2Device(keymaster2_device_t* d) : KeymasterDevice<keymaster2_device_t>{d} {}
+ ~Keymaster2Device() override final { keymaster2_close(mDevice); }
+ keymaster_error_t finish(keymaster_operation_handle_t operation_handle,
+ const keymaster_key_param_set_t* in_params,
+ const keymaster_blob_t* signature,
+ keymaster_key_param_set_t* out_params,
+ keymaster_blob_t* output) const override final {
+ return mDevice->finish(mDevice, operation_handle, in_params, nullptr, signature, out_params,
+ output);
+ }
+};
+
+KeymasterOperation::~KeymasterOperation() {
+ if (mDevice) mDevice->abort(mOpHandle);
+}
+
+bool KeymasterOperation::updateCompletely(const std::string& input, std::string* output) {
+ output->clear();
+ auto it = input.begin();
+ while (it != input.end()) {
+ size_t toRead = static_cast<size_t>(input.end() - it);
+ keymaster_blob_t inputBlob{reinterpret_cast<const uint8_t*>(&*it), toRead};
+ keymaster_blob_t outputBlob;
+ size_t inputConsumed;
+ auto error =
+ mDevice->update(mOpHandle, nullptr, &inputBlob, &inputConsumed, nullptr, &outputBlob);
+ if (error != KM_ERROR_OK) {
+ LOG(ERROR) << "update failed, code " << error;
+ mDevice = nullptr;
+ return false;
+ }
+ output->append(reinterpret_cast<const char*>(outputBlob.data), outputBlob.data_length);
+ free(const_cast<uint8_t*>(outputBlob.data));
+ if (inputConsumed > toRead) {
+ LOG(ERROR) << "update reported too much input consumed";
+ mDevice = nullptr;
+ return false;
+ }
+ it += inputConsumed;
+ }
+ return true;
+}
+
+bool KeymasterOperation::finish() {
+ auto error = mDevice->finish(mOpHandle, nullptr, nullptr, nullptr, nullptr);
+ mDevice = nullptr;
+ if (error != KM_ERROR_OK) {
+ LOG(ERROR) << "finish failed, code " << error;
+ return false;
+ }
+ return true;
+}
+
+bool KeymasterOperation::finishWithOutput(std::string* output) {
+ keymaster_blob_t outputBlob;
+ auto error = mDevice->finish(mOpHandle, nullptr, nullptr, nullptr, &outputBlob);
+ mDevice = nullptr;
+ if (error != KM_ERROR_OK) {
+ LOG(ERROR) << "finish failed, code " << error;
+ return false;
+ }
+ output->assign(reinterpret_cast<const char*>(outputBlob.data), outputBlob.data_length);
+ free(const_cast<uint8_t*>(outputBlob.data));
+ return true;
+}
+
+Keymaster::Keymaster() {
+ mDevice = nullptr;
+ const hw_module_t* module;
+ int ret = hw_get_module_by_class(KEYSTORE_HARDWARE_MODULE_ID, NULL, &module);
+ if (ret != 0) {
+ LOG(ERROR) << "hw_get_module_by_class returned " << ret;
+ return;
+ }
+ if (module->module_api_version == KEYMASTER_MODULE_API_VERSION_1_0) {
+ keymaster1_device_t* device;
+ ret = keymaster1_open(module, &device);
+ if (ret != 0) {
+ LOG(ERROR) << "keymaster1_open returned " << ret;
+ return;
+ }
+ mDevice = std::make_shared<Keymaster1Device>(device);
+ } else if (module->module_api_version == KEYMASTER_MODULE_API_VERSION_2_0) {
+ keymaster2_device_t* device;
+ ret = keymaster2_open(module, &device);
+ if (ret != 0) {
+ LOG(ERROR) << "keymaster2_open returned " << ret;
+ return;
+ }
+ mDevice = std::make_shared<Keymaster2Device>(device);
+ } else {
+ LOG(ERROR) << "module_api_version is " << module->module_api_version;
+ return;
+ }
+}
+
+/*bool Keymaster::generateKey(const keymaster::AuthorizationSet& inParams, std::string* key) {
+ keymaster_key_blob_t keyBlob;
+ auto error = mDevice->generate_key(&inParams, &keyBlob);
+ if (error != KM_ERROR_OK) {
+ LOG(ERROR) << "generate_key failed, code " << error;
+ return false;
+ }
+ key->assign(reinterpret_cast<const char*>(keyBlob.key_material), keyBlob.key_material_size);
+ free(const_cast<uint8_t*>(keyBlob.key_material));
+ return true;
+}*/
+
+bool Keymaster::deleteKey(const std::string& key) {
+ keymaster_key_blob_t keyBlob{reinterpret_cast<const uint8_t*>(key.data()), key.size()};
+ auto error = mDevice->delete_key(&keyBlob);
+ if (error != KM_ERROR_OK) {
+ LOG(ERROR) << "delete_key failed, code " << error;
+ return false;
+ }
+ return true;
+}
+
+KeymasterOperation Keymaster::begin(keymaster_purpose_t purpose, const std::string& key,
+ const keymaster::AuthorizationSet& inParams,
+ keymaster::AuthorizationSet* outParams) {
+ keymaster_key_blob_t keyBlob{reinterpret_cast<const uint8_t*>(key.data()), key.size()};
+ keymaster_operation_handle_t mOpHandle;
+ keymaster_key_param_set_t outParams_set;
+ auto error = mDevice->begin(purpose, &keyBlob, &inParams, &outParams_set, &mOpHandle);
+ if (error != KM_ERROR_OK) {
+ LOG(ERROR) << "begin failed, code " << error;
+ return KeymasterOperation(nullptr, mOpHandle);
+ }
+ outParams->Clear();
+ outParams->push_back(outParams_set);
+ keymaster_free_param_set(&outParams_set);
+ return KeymasterOperation(mDevice, mOpHandle);
+}
+
+KeymasterOperation Keymaster::begin(keymaster_purpose_t purpose, const std::string& key,
+ const keymaster::AuthorizationSet& inParams) {
+ keymaster_key_blob_t keyBlob{reinterpret_cast<const uint8_t*>(key.data()), key.size()};
+ keymaster_operation_handle_t mOpHandle;
+ auto error = mDevice->begin(purpose, &keyBlob, &inParams, nullptr, &mOpHandle);
+ if (error != KM_ERROR_OK) {
+ LOG(ERROR) << "begin failed, code " << error;
+ return KeymasterOperation(nullptr, mOpHandle);
+ }
+ return KeymasterOperation(mDevice, mOpHandle);
+}
+
+} // namespace vold
+} // namespace android
diff --git a/crypto/ext4crypt/Keymaster.h b/crypto/ext4crypt/Keymaster.h
new file mode 100644
index 0000000..bd3f219
--- /dev/null
+++ b/crypto/ext4crypt/Keymaster.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_VOLD_KEYMASTER_H
+#define ANDROID_VOLD_KEYMASTER_H
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include <keymaster/authorization_set.h>
+#include "Utils.h"
+
+namespace android {
+namespace vold {
+
+using namespace keymaster;
+
+// C++ wrappers to the Keymaster C interface.
+// This is tailored to the needs of KeyStorage, but could be extended to be
+// a more general interface.
+
+// Class that wraps a keymaster1_device_t or keymaster2_device_t and provides methods
+// they have in common. Also closes the device on destruction.
+class IKeymasterDevice;
+
+// Wrapper for a keymaster_operation_handle_t representing an
+// ongoing Keymaster operation. Aborts the operation
+// in the destructor if it is unfinished. Methods log failures
+// to LOG(ERROR).
+class KeymasterOperation {
+ public:
+ ~KeymasterOperation();
+ // Is this instance valid? This is false if creation fails, and becomes
+ // false on finish or if an update fails.
+ explicit operator bool() { return mDevice != nullptr; }
+ // Call "update" repeatedly until all of the input is consumed, and
+ // concatenate the output. Return true on success.
+ bool updateCompletely(const std::string& input, std::string* output);
+ // Finish; pass nullptr for the "output" param.
+ bool finish();
+ // Finish and write the output to this string.
+ bool finishWithOutput(std::string* output);
+ // Move constructor
+ KeymasterOperation(KeymasterOperation&& rhs) {
+ mOpHandle = std::move(rhs.mOpHandle);
+ mDevice = std::move(rhs.mDevice);
+ }
+
+ private:
+ KeymasterOperation(std::shared_ptr<IKeymasterDevice> d, keymaster_operation_handle_t h)
+ : mDevice{d}, mOpHandle{h} {}
+ std::shared_ptr<IKeymasterDevice> mDevice;
+ keymaster_operation_handle_t mOpHandle;
+ DISALLOW_COPY_AND_ASSIGN(KeymasterOperation);
+ friend class Keymaster;
+};
+
+// Wrapper for a Keymaster device for methods that start a KeymasterOperation or are not
+// part of one.
+class Keymaster {
+ public:
+ Keymaster();
+ // false if we failed to open the keymaster device.
+ explicit operator bool() { return mDevice != nullptr; }
+ // Generate a key in the keymaster from the given params.
+ //bool generateKey(const AuthorizationSet& inParams, std::string* key);
+ // If the keymaster supports it, permanently delete a key.
+ bool deleteKey(const std::string& key);
+ // Begin a new cryptographic operation, collecting output parameters.
+ KeymasterOperation begin(keymaster_purpose_t purpose, const std::string& key,
+ const AuthorizationSet& inParams, AuthorizationSet* outParams);
+ // Begin a new cryptographic operation; don't collect output parameters.
+ KeymasterOperation begin(keymaster_purpose_t purpose, const std::string& key,
+ const AuthorizationSet& inParams);
+
+ private:
+ std::shared_ptr<IKeymasterDevice> mDevice;
+ DISALLOW_COPY_AND_ASSIGN(Keymaster);
+};
+
+template <keymaster_tag_t Tag>
+inline AuthorizationSetBuilder& addStringParam(AuthorizationSetBuilder&& params,
+ TypedTag<KM_BYTES, Tag> tag,
+ const std::string& val) {
+ return params.Authorization(tag, val.data(), val.size());
+}
+
+template <keymaster_tag_t Tag>
+inline void addStringParam(AuthorizationSetBuilder* params, TypedTag<KM_BYTES, Tag> tag,
+ const std::string& val) {
+ params->Authorization(tag, val.data(), val.size());
+}
+
+} // namespace vold
+} // namespace android
+
+#endif
diff --git a/crypto/ext4crypt/Keymaster3.cpp b/crypto/ext4crypt/Keymaster3.cpp
new file mode 100644
index 0000000..7862044
--- /dev/null
+++ b/crypto/ext4crypt/Keymaster3.cpp
@@ -0,0 +1,348 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Keymaster3.h"
+
+//#include <android-base/logging.h>
+#include <keystore/keymaster_tags.h>
+#include <keystore/authorization_set.h>
+#include <keystore/keystore_hidl_support.h>
+
+#include <iostream>
+#define ERROR 1
+#define LOG(x) std::cout
+
+using namespace ::keystore;
+using android::hardware::hidl_string;
+
+namespace android {
+namespace vold {
+
+KeymasterOperation::~KeymasterOperation() {
+ if (mDevice.get()) mDevice->abort(mOpHandle);
+}
+
+bool KeymasterOperation::updateCompletely(const std::string& input, std::string* output) {
+ if (output)
+ output->clear();
+ auto it = input.begin();
+ uint32_t inputConsumed;
+
+ ErrorCode km_error;
+ auto hidlCB = [&] (ErrorCode ret, uint32_t _inputConsumed,
+ const hidl_vec<KeyParameter>& /*ignored*/, const hidl_vec<uint8_t>& _output) {
+ km_error = ret;
+ if (km_error != ErrorCode::OK) return;
+ inputConsumed = _inputConsumed;
+ if (output)
+ output->append(reinterpret_cast<const char*>(&_output[0]), _output.size());
+ };
+
+ while (it != input.end()) {
+ size_t toRead = static_cast<size_t>(input.end() - it);
+ auto inputBlob = blob2hidlVec(reinterpret_cast<const uint8_t*>(&*it), toRead);
+ auto error = mDevice->update(mOpHandle, hidl_vec<KeyParameter>(), inputBlob, hidlCB);
+ if (!error.isOk()) {
+ LOG(ERROR) << "update failed: " << error.description();
+ mDevice = nullptr;
+ return false;
+ }
+ if (km_error != ErrorCode::OK) {
+ LOG(ERROR) << "update failed, code " << int32_t(km_error);
+ mDevice = nullptr;
+ return false;
+ }
+ if (inputConsumed > toRead) {
+ LOG(ERROR) << "update reported too much input consumed";
+ mDevice = nullptr;
+ return false;
+ }
+ it += inputConsumed;
+ }
+ return true;
+}
+
+bool KeymasterOperation::finish(std::string* output) {
+ ErrorCode km_error;
+ auto hidlCb = [&] (ErrorCode ret, const hidl_vec<KeyParameter>& /*ignored*/,
+ const hidl_vec<uint8_t>& _output) {
+ km_error = ret;
+ if (km_error != ErrorCode::OK) return;
+ if (output)
+ output->assign(reinterpret_cast<const char*>(&_output[0]), _output.size());
+ };
+ auto error = mDevice->finish(mOpHandle, hidl_vec<KeyParameter>(), hidl_vec<uint8_t>(),
+ hidl_vec<uint8_t>(), hidlCb);
+ mDevice = nullptr;
+ if (!error.isOk()) {
+ LOG(ERROR) << "finish failed: " << error.description();
+ return false;
+ }
+ if (km_error != ErrorCode::OK) {
+ LOG(ERROR) << "finish failed, code " << int32_t(km_error);
+ return false;
+ }
+ return true;
+}
+
+Keymaster::Keymaster() {
+ mDevice = ::android::hardware::keymaster::V3_0::IKeymasterDevice::getService();
+}
+
+/*bool Keymaster::generateKey(const AuthorizationSet& inParams, std::string* key) {
+ ErrorCode km_error;
+ auto hidlCb = [&] (ErrorCode ret, const hidl_vec<uint8_t>& keyBlob,
+ const KeyCharacteristics& /*ignored* /) {
+ km_error = ret;
+ if (km_error != ErrorCode::OK) return;
+ if (key)
+ key->assign(reinterpret_cast<const char*>(&keyBlob[0]), keyBlob.size());
+ };
+
+ auto error = mDevice->generateKey(inParams.hidl_data(), hidlCb);
+ if (!error.isOk()) {
+ LOG(ERROR) << "generate_key failed: " << error.description();
+ return false;
+ }
+ if (km_error != ErrorCode::OK) {
+ LOG(ERROR) << "generate_key failed, code " << int32_t(km_error);
+ return false;
+ }
+ return true;
+}*/
+
+bool Keymaster::deleteKey(const std::string& key) {
+ LOG(ERROR) << "NOT deleting key in TWRP";
+ return false;
+ /*auto keyBlob = blob2hidlVec(key);
+ auto error = mDevice->deleteKey(keyBlob);
+ if (!error.isOk()) {
+ LOG(ERROR) << "delete_key failed: " << error.description();
+ return false;
+ }
+ if (ErrorCode(error) != ErrorCode::OK) {
+ LOG(ERROR) << "delete_key failed, code " << uint32_t(ErrorCode(error));
+ return false;
+ }
+ return true;*/
+}
+
+bool Keymaster::upgradeKey(const std::string& oldKey, const AuthorizationSet& inParams,
+ std::string* newKey) {
+ auto oldKeyBlob = blob2hidlVec(oldKey);
+ ErrorCode km_error;
+ auto hidlCb = [&] (ErrorCode ret, const hidl_vec<uint8_t>& upgradedKeyBlob) {
+ km_error = ret;
+ if (km_error != ErrorCode::OK) return;
+ if (newKey)
+ newKey->assign(reinterpret_cast<const char*>(&upgradedKeyBlob[0]),
+ upgradedKeyBlob.size());
+ };
+ auto error = mDevice->upgradeKey(oldKeyBlob, inParams.hidl_data(), hidlCb);
+ if (!error.isOk()) {
+ LOG(ERROR) << "upgrade_key failed: " << error.description();
+ return false;
+ }
+ if (km_error != ErrorCode::OK) {
+ LOG(ERROR) << "upgrade_key failed, code " << int32_t(km_error);
+ return false;
+ }
+ return true;
+}
+
+KeymasterOperation Keymaster::begin(KeyPurpose purpose, const std::string& key,
+ const AuthorizationSet& inParams,
+ AuthorizationSet* outParams) {
+ auto keyBlob = blob2hidlVec(key);
+ uint64_t mOpHandle;
+ ErrorCode km_error;
+
+ auto hidlCb = [&] (ErrorCode ret, const hidl_vec<KeyParameter>& _outParams,
+ uint64_t operationHandle) {
+ km_error = ret;
+ if (km_error != ErrorCode::OK) return;
+ if (outParams)
+ *outParams = _outParams;
+ mOpHandle = operationHandle;
+ };
+
+ auto error = mDevice->begin(purpose, keyBlob, inParams.hidl_data(), hidlCb);
+ if (!error.isOk()) {
+ LOG(ERROR) << "begin failed: " << error.description();
+ return KeymasterOperation(ErrorCode::UNKNOWN_ERROR);
+ }
+ if (km_error != ErrorCode::OK) {
+ LOG(ERROR) << "begin failed, code " << int32_t(km_error);
+ return KeymasterOperation(km_error);
+ }
+ return KeymasterOperation(mDevice, mOpHandle);
+}
+bool Keymaster::isSecure() {
+ bool _isSecure = false;
+ auto rc = mDevice->getHardwareFeatures(
+ [&] (bool isSecure, bool, bool, bool, bool, const hidl_string&, const hidl_string&) {
+ _isSecure = isSecure; });
+ return rc.isOk() && _isSecure;
+}
+
+} // namespace vold
+} // namespace android
+
+using namespace ::android::vold;
+
+/*
+int keymaster_compatibility_cryptfs_scrypt() {
+ Keymaster dev;
+ if (!dev) {
+ LOG(ERROR) << "Failed to initiate keymaster session";
+ return -1;
+ }
+ return dev.isSecure();
+}
+*/
+
+/*int keymaster_create_key_for_cryptfs_scrypt(uint32_t rsa_key_size,
+ uint64_t rsa_exponent,
+ uint32_t ratelimit,
+ uint8_t* key_buffer,
+ uint32_t key_buffer_size,
+ uint32_t* key_out_size)
+{
+ Keymaster dev;
+ std::string key;
+ if (!dev) {
+ LOG(ERROR) << "Failed to initiate keymaster session";
+ return -1;
+ }
+ if (!key_buffer || !key_out_size) {
+ LOG(ERROR) << __FILE__ << ":" << __LINE__ << ":Invalid argument";
+ return -1;
+ }
+ if (key_out_size) {
+ *key_out_size = 0;
+ }
+
+ auto paramBuilder = AuthorizationSetBuilder()
+ .Authorization(TAG_ALGORITHM, Algorithm::RSA)
+ .Authorization(TAG_KEY_SIZE, rsa_key_size)
+ .Authorization(TAG_RSA_PUBLIC_EXPONENT, rsa_exponent)
+ .Authorization(TAG_PURPOSE, KeyPurpose::SIGN)
+ .Authorization(TAG_PADDING, PaddingMode::NONE)
+ .Authorization(TAG_DIGEST, Digest::NONE)
+ .Authorization(TAG_BLOB_USAGE_REQUIREMENTS,
+ KeyBlobUsageRequirements::STANDALONE)
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .Authorization(TAG_MIN_SECONDS_BETWEEN_OPS, ratelimit);
+
+ if (!dev.generateKey(paramBuilder, &key)) {
+ return -1;
+ }
+
+ if (key_out_size) {
+ *key_out_size = key.size();
+ }
+
+ if (key_buffer_size < key.size()) {
+ return -1;
+ }
+
+ std::copy(key.data(), key.data() + key.size(), key_buffer);
+ return 0;
+}*/
+
+int keymaster_sign_object_for_cryptfs_scrypt(const uint8_t* key_blob,
+ size_t key_blob_size,
+ uint32_t ratelimit,
+ const uint8_t* object,
+ const size_t object_size,
+ uint8_t** signature_buffer,
+ size_t* signature_buffer_size,
+ uint8_t* key_buffer,
+ uint32_t key_buffer_size,
+ uint32_t* key_out_size)
+{
+ Keymaster dev;
+ if (!dev) {
+ LOG(ERROR) << "Failed to initiate keymaster session";
+ return -1;
+ }
+ if (!key_blob || !object || !signature_buffer || !signature_buffer_size) {
+ LOG(ERROR) << __FILE__ << ":" << __LINE__ << ":Invalid argument";
+ return -1;
+ }
+
+ AuthorizationSet outParams;
+ std::string key(reinterpret_cast<const char*>(key_blob), key_blob_size);
+ std::string input(reinterpret_cast<const char*>(object), object_size);
+ std::string output;
+ KeymasterOperation op;
+
+ auto paramBuilder = AuthorizationSetBuilder()
+ .Authorization(TAG_PADDING, PaddingMode::NONE)
+ .Authorization(TAG_DIGEST, Digest::NONE);
+
+ while (true) {
+ op = dev.begin(KeyPurpose::SIGN, key, paramBuilder, &outParams);
+ if (op.errorCode() == ErrorCode::KEY_RATE_LIMIT_EXCEEDED) {
+ sleep(ratelimit);
+ continue;
+ } else if (op.errorCode() == ErrorCode::KEY_REQUIRES_UPGRADE) {
+ std::string newKey;
+ bool ret = dev.upgradeKey(key, paramBuilder, &newKey);
+ if(ret == false) {
+ LOG(ERROR) << "Error upgradeKey: ";
+ return -1;
+ }
+
+ if (key_out_size) {
+ *key_out_size = newKey.size();
+ }
+
+ if (key_buffer_size < newKey.size()) {
+ LOG(ERROR) << "key buffer size is too small";
+ return -1;
+ }
+
+ std::copy(newKey.data(), newKey.data() + newKey.size(), key_buffer);
+ key = newKey;
+ } else break;
+ }
+
+ if (op.errorCode() != ErrorCode::OK) {
+ LOG(ERROR) << "Error starting keymaster signature transaction: " << int32_t(op.errorCode());
+ return -1;
+ }
+
+ if (!op.updateCompletely(input, &output)) {
+ LOG(ERROR) << "Error sending data to keymaster signature transaction: "
+ << uint32_t(op.errorCode());
+ return -1;
+ }
+
+ if (!op.finish(&output)) {
+ LOG(ERROR) << "Error finalizing keymaster signature transaction: " << int32_t(op.errorCode());
+ return -1;
+ }
+
+ *signature_buffer = reinterpret_cast<uint8_t*>(malloc(output.size()));
+ if (*signature_buffer == nullptr) {
+ LOG(ERROR) << "Error allocation buffer for keymaster signature";
+ return -1;
+ }
+ *signature_buffer_size = output.size();
+ std::copy(output.data(), output.data() + output.size(), *signature_buffer);
+ return 0;
+}
diff --git a/crypto/ext4crypt/Keymaster3.h b/crypto/ext4crypt/Keymaster3.h
new file mode 100644
index 0000000..cb5b644
--- /dev/null
+++ b/crypto/ext4crypt/Keymaster3.h
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_VOLD_KEYMASTER_H
+#define ANDROID_VOLD_KEYMASTER_H
+
+#ifdef __cplusplus
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include <android/hardware/keymaster/3.0/IKeymasterDevice.h>
+#include <keystore/authorization_set.h>
+#include "Utils.h"
+
+namespace android {
+namespace vold {
+using ::android::hardware::keymaster::V3_0::IKeymasterDevice;
+using ::keystore::ErrorCode;
+using ::keystore::KeyPurpose;
+using ::keystore::AuthorizationSet;
+
+// C++ wrappers to the Keymaster hidl interface.
+// This is tailored to the needs of KeyStorage, but could be extended to be
+// a more general interface.
+
+// Wrapper for a Keymaster operation handle representing an
+// ongoing Keymaster operation. Aborts the operation
+// in the destructor if it is unfinished. Methods log failures
+// to LOG(ERROR).
+class KeymasterOperation {
+ public:
+ ~KeymasterOperation();
+ // Is this instance valid? This is false if creation fails, and becomes
+ // false on finish or if an update fails.
+ explicit operator bool() { return mError == ErrorCode::OK; }
+ ErrorCode errorCode() { return mError; }
+ // Call "update" repeatedly until all of the input is consumed, and
+ // concatenate the output. Return true on success.
+ bool updateCompletely(const std::string& input, std::string* output);
+ // Finish and write the output to this string, unless pointer is null.
+ bool finish(std::string* output);
+ // Move constructor
+ KeymasterOperation(KeymasterOperation&& rhs) {
+ mDevice = std::move(rhs.mDevice);
+ mOpHandle = std::move(rhs.mOpHandle);
+ mError = std::move(rhs.mError);
+ }
+ // Construct an object in an error state for error returns
+ KeymasterOperation()
+ : mDevice{nullptr}, mOpHandle{0},
+ mError {ErrorCode::UNKNOWN_ERROR} {}
+ // Move Assignment
+ KeymasterOperation& operator= (KeymasterOperation&& rhs) {
+ mDevice = std::move(rhs.mDevice);
+ mOpHandle = std::move(rhs.mOpHandle);
+ mError = std::move(rhs.mError);
+ rhs.mError = ErrorCode::UNKNOWN_ERROR;
+ rhs.mOpHandle = 0;
+ return *this;
+ }
+
+ private:
+ KeymasterOperation(const sp<IKeymasterDevice>& d, uint64_t h)
+ : mDevice{d}, mOpHandle{h}, mError {ErrorCode::OK} {}
+ KeymasterOperation(ErrorCode error)
+ : mDevice{nullptr}, mOpHandle{0},
+ mError {error} {}
+ sp<IKeymasterDevice> mDevice;
+ uint64_t mOpHandle;
+ ErrorCode mError;
+ DISALLOW_COPY_AND_ASSIGN(KeymasterOperation);
+ friend class Keymaster;
+};
+
+// Wrapper for a Keymaster device for methods that start a KeymasterOperation or are not
+// part of one.
+class Keymaster {
+ public:
+ Keymaster();
+ // false if we failed to open the keymaster device.
+ explicit operator bool() { return mDevice.get() != nullptr; }
+ // Generate a key in the keymaster from the given params.
+ //bool generateKey(const AuthorizationSet& inParams, std::string* key);
+ // If the keymaster supports it, permanently delete a key.
+ bool deleteKey(const std::string& key);
+ // Replace stored key blob in response to KM_ERROR_KEY_REQUIRES_UPGRADE.
+ bool upgradeKey(const std::string& oldKey, const AuthorizationSet& inParams,
+ std::string* newKey);
+ // Begin a new cryptographic operation, collecting output parameters if pointer is non-null
+ KeymasterOperation begin(KeyPurpose purpose, const std::string& key,
+ const AuthorizationSet& inParams, AuthorizationSet* outParams);
+ bool isSecure();
+
+ private:
+ sp<hardware::keymaster::V3_0::IKeymasterDevice> mDevice;
+ DISALLOW_COPY_AND_ASSIGN(Keymaster);
+};
+
+} // namespace vold
+} // namespace android
+
+#endif // __cplusplus
+
+
+/*
+ * The following functions provide C bindings to keymaster services
+ * needed by cryptfs scrypt. The compatibility check checks whether
+ * the keymaster implementation is considered secure, i.e., TEE backed.
+ * The create_key function generates an RSA key for signing.
+ * The sign_object function signes an object with the given keymaster
+ * key.
+ */
+__BEGIN_DECLS
+
+//int keymaster_compatibility_cryptfs_scrypt();
+/*int keymaster_create_key_for_cryptfs_scrypt(uint32_t rsa_key_size,
+ uint64_t rsa_exponent,
+ uint32_t ratelimit,
+ uint8_t* key_buffer,
+ uint32_t key_buffer_size,
+ uint32_t* key_out_size);*/
+
+int keymaster_sign_object_for_cryptfs_scrypt(const uint8_t* key_blob,
+ size_t key_blob_size,
+ uint32_t ratelimit,
+ const uint8_t* object,
+ const size_t object_size,
+ uint8_t** signature_buffer,
+ size_t* signature_buffer_size,
+ uint8_t* key_buffer,
+ uint32_t key_buffer_size,
+ uint32_t* key_out_size);
+
+__END_DECLS
+
+#endif
diff --git a/crypto/ext4crypt/Keymaster4.cpp b/crypto/ext4crypt/Keymaster4.cpp
new file mode 100644
index 0000000..6507bb1
--- /dev/null
+++ b/crypto/ext4crypt/Keymaster4.cpp
@@ -0,0 +1,382 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Keymaster4.h"
+
+//#include <android-base/logging.h>
+#include <keymasterV4_0/authorization_set.h>
+#include <keymasterV4_0/keymaster_utils.h>
+
+#include <iostream>
+#define LOG(x) std::cout
+#define PLOG(x) std::cout
+
+namespace android {
+namespace vold {
+
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::keymaster::V4_0::SecurityLevel;
+
+KeymasterOperation::~KeymasterOperation() {
+ if (mDevice) mDevice->abort(mOpHandle);
+}
+
+bool KeymasterOperation::updateCompletely(const char* input, size_t inputLen,
+ const std::function<void(const char*, size_t)> consumer) {
+ uint32_t inputConsumed = 0;
+
+ km::ErrorCode km_error;
+ auto hidlCB = [&](km::ErrorCode ret, uint32_t inputConsumedDelta,
+ const hidl_vec<km::KeyParameter>& /*ignored*/,
+ const hidl_vec<uint8_t>& _output) {
+ km_error = ret;
+ if (km_error != km::ErrorCode::OK) return;
+ inputConsumed += inputConsumedDelta;
+ consumer(reinterpret_cast<const char*>(&_output[0]), _output.size());
+ };
+
+ while (inputConsumed != inputLen) {
+ size_t toRead = static_cast<size_t>(inputLen - inputConsumed);
+ auto inputBlob = km::support::blob2hidlVec(
+ reinterpret_cast<const uint8_t*>(&input[inputConsumed]), toRead);
+ auto error = mDevice->update(mOpHandle, hidl_vec<km::KeyParameter>(), inputBlob,
+ km::HardwareAuthToken(), km::VerificationToken(), hidlCB);
+ if (!error.isOk()) {
+ LOG(ERROR) << "update failed: " << error.description() << std::endl;
+ mDevice = nullptr;
+ return false;
+ }
+ if (km_error != km::ErrorCode::OK) {
+ LOG(ERROR) << "update failed, code " << int32_t(km_error) << std::endl;
+ mDevice = nullptr;
+ return false;
+ }
+ if (inputConsumed > inputLen) {
+ LOG(ERROR) << "update reported too much input consumed" << std::endl;
+ mDevice = nullptr;
+ return false;
+ }
+ }
+ return true;
+}
+
+bool KeymasterOperation::finish(std::string* output) {
+ km::ErrorCode km_error;
+ auto hidlCb = [&](km::ErrorCode ret, const hidl_vec<km::KeyParameter>& /*ignored*/,
+ const hidl_vec<uint8_t>& _output) {
+ km_error = ret;
+ if (km_error != km::ErrorCode::OK) return;
+ if (output) output->assign(reinterpret_cast<const char*>(&_output[0]), _output.size());
+ };
+ auto error = mDevice->finish(mOpHandle, hidl_vec<km::KeyParameter>(), hidl_vec<uint8_t>(),
+ hidl_vec<uint8_t>(), km::HardwareAuthToken(),
+ km::VerificationToken(), hidlCb);
+ mDevice = nullptr;
+ if (!error.isOk()) {
+ LOG(ERROR) << "finish failed: " << error.description() << std::endl;
+ return false;
+ }
+ if (km_error != km::ErrorCode::OK) {
+ LOG(ERROR) << "finish failed, code " << int32_t(km_error) << std::endl;
+ return false;
+ }
+ return true;
+}
+
+/* static */ bool Keymaster::hmacKeyGenerated = false;
+
+Keymaster::Keymaster() {
+ auto devices = KmDevice::enumerateAvailableDevices();
+ if (!hmacKeyGenerated) {
+ KmDevice::performHmacKeyAgreement(devices);
+ hmacKeyGenerated = true;
+ }
+ for (auto& dev : devices) {
+ // Do not use StrongBox for device encryption / credential encryption. If a security chip
+ // is present it will have Weaver, which already strengthens CE. We get no additional
+ // benefit from using StrongBox here, so skip it.
+ if (dev->halVersion().securityLevel != SecurityLevel::STRONGBOX) {
+ mDevice = std::move(dev);
+ break;
+ }
+ }
+ if (!mDevice) return;
+ auto& version = mDevice->halVersion();
+ LOG(INFO) << "Using " << version.keymasterName << " from " << version.authorName
+ << " for encryption. Security level: " << toString(version.securityLevel)
+ << ", HAL: " << mDevice->descriptor() << "/" << mDevice->instanceName() << std::endl;
+}
+
+bool Keymaster::generateKey(const km::AuthorizationSet& inParams, std::string* key) {
+ km::ErrorCode km_error;
+ auto hidlCb = [&](km::ErrorCode ret, const hidl_vec<uint8_t>& keyBlob,
+ const km::KeyCharacteristics& /*ignored*/) {
+ km_error = ret;
+ if (km_error != km::ErrorCode::OK) return;
+ if (key) key->assign(reinterpret_cast<const char*>(&keyBlob[0]), keyBlob.size());
+ };
+
+ auto error = mDevice->generateKey(inParams.hidl_data(), hidlCb);
+ if (!error.isOk()) {
+ LOG(ERROR) << "generate_key failed: " << error.description() << std::endl;
+ return false;
+ }
+ if (km_error != km::ErrorCode::OK) {
+ LOG(ERROR) << "generate_key failed, code " << int32_t(km_error) << std::endl;
+ return false;
+ }
+ return true;
+}
+
+km::ErrorCode Keymaster::exportKey(km::KeyFormat format, KeyBuffer& kmKey, const std::string& clientId,
+ const std::string& appData, std::string* key) {
+ auto kmKeyBlob = km::support::blob2hidlVec(std::string(kmKey.data(), kmKey.size()));
+ auto emptyAssign = NULL;
+ auto kmClientId = (clientId == "!") ? emptyAssign: km::support::blob2hidlVec(clientId);
+ auto kmAppData = (appData == "!") ? emptyAssign: km::support::blob2hidlVec(appData);
+ km::ErrorCode km_error;
+ auto hidlCb = [&](km::ErrorCode ret, const hidl_vec<uint8_t>& exportedKeyBlob) {
+ km_error = ret;
+ if (km_error != km::ErrorCode::OK) return;
+ if(key)
+ key->assign(reinterpret_cast<const char*>(&exportedKeyBlob[0]),
+ exportedKeyBlob.size());
+ };
+ auto error = mDevice->exportKey(format, kmKeyBlob, kmClientId, kmAppData, hidlCb);
+ if (!error.isOk()) {
+ LOG(ERROR) << "export_key failed: " << error.description();
+ return km::ErrorCode::UNKNOWN_ERROR;
+ }
+ if (km_error != km::ErrorCode::OK) {
+ LOG(ERROR) << "export_key failed, code " << int32_t(km_error);
+ return km_error;
+ }
+ return km::ErrorCode::OK;
+}
+
+bool Keymaster::deleteKey(const std::string& key) {
+ LOG(ERROR) << "not actually deleting key\n";
+ return true;
+ auto keyBlob = km::support::blob2hidlVec(key);
+ auto error = mDevice->deleteKey(keyBlob);
+ if (!error.isOk()) {
+ LOG(ERROR) << "delete_key failed: " << error.description();
+ return false;
+ }
+ if (error != km::ErrorCode::OK) {
+ LOG(ERROR) << "delete_key failed, code " << int32_t(km::ErrorCode(error));
+ return false;
+ }
+ return true;
+}
+
+bool Keymaster::upgradeKey(const std::string& oldKey, const km::AuthorizationSet& inParams,
+ std::string* newKey) {
+ auto oldKeyBlob = km::support::blob2hidlVec(oldKey);
+ km::ErrorCode km_error;
+ auto hidlCb = [&](km::ErrorCode ret, const hidl_vec<uint8_t>& upgradedKeyBlob) {
+ km_error = ret;
+ if (km_error != km::ErrorCode::OK) return;
+ if (newKey)
+ newKey->assign(reinterpret_cast<const char*>(&upgradedKeyBlob[0]),
+ upgradedKeyBlob.size());
+ };
+ auto error = mDevice->upgradeKey(oldKeyBlob, inParams.hidl_data(), hidlCb);
+ if (!error.isOk()) {
+ LOG(ERROR) << "upgrade_key failed: " << error.description() << std::endl;
+ return false;
+ }
+ if (km_error != km::ErrorCode::OK) {
+ LOG(ERROR) << "upgrade_key failed, code " << int32_t(km_error) << std::endl;
+ return false;
+ }
+ return true;
+}
+
+KeymasterOperation Keymaster::begin(km::KeyPurpose purpose, const std::string& key,
+ const km::AuthorizationSet& inParams,
+ const km::HardwareAuthToken& authToken,
+ km::AuthorizationSet* outParams) {
+ auto keyBlob = km::support::blob2hidlVec(key);
+ uint64_t mOpHandle;
+ km::ErrorCode km_error;
+
+ auto hidlCb = [&](km::ErrorCode ret, const hidl_vec<km::KeyParameter>& _outParams,
+ uint64_t operationHandle) {
+ km_error = ret;
+ if (km_error != km::ErrorCode::OK) return;
+ if (outParams) *outParams = _outParams;
+ mOpHandle = operationHandle;
+ };
+
+ auto error = mDevice->begin(purpose, keyBlob, inParams.hidl_data(), authToken, hidlCb);
+ if (!error.isOk()) {
+ LOG(ERROR) << "begin failed: " << error.description() << std::endl;
+ return KeymasterOperation(km::ErrorCode::UNKNOWN_ERROR);
+ }
+ if (km_error != km::ErrorCode::OK) {
+ LOG(ERROR) << "begin failed, code " << int32_t(km_error) << std::endl;
+ return KeymasterOperation(km_error);
+ }
+ return KeymasterOperation(mDevice.get(), mOpHandle);
+}
+
+bool Keymaster::isSecure() {
+ return mDevice->halVersion().securityLevel != km::SecurityLevel::SOFTWARE;
+}
+
+} // namespace vold
+} // namespace android
+
+using namespace ::android::vold;
+
+/*
+int keymaster_compatibility_cryptfs_scrypt() {
+ Keymaster dev;
+ if (!dev) {
+ LOG(ERROR) << "Failed to initiate keymaster session" << std::endl;
+ return -1;
+ }
+ return dev.isSecure();
+}
+*/
+
+static bool write_string_to_buf(const std::string& towrite, uint8_t* buffer, uint32_t buffer_size,
+ uint32_t* out_size) {
+ if (!buffer || !out_size) {
+ LOG(ERROR) << "Missing target pointers" << std::endl;
+ return false;
+ }
+ *out_size = towrite.size();
+ if (buffer_size < towrite.size()) {
+ LOG(ERROR) << "Buffer too small " << buffer_size << " < " << towrite.size() << std::endl;
+ return false;
+ }
+ memset(buffer, '\0', buffer_size);
+ std::copy(towrite.begin(), towrite.end(), buffer);
+ return true;
+}
+
+static km::AuthorizationSet keyParams(uint32_t rsa_key_size, uint64_t rsa_exponent,
+ uint32_t ratelimit) {
+ return km::AuthorizationSetBuilder()
+ .RsaSigningKey(rsa_key_size, rsa_exponent)
+ .NoDigestOrPadding()
+ .Authorization(km::TAG_BLOB_USAGE_REQUIREMENTS, km::KeyBlobUsageRequirements::STANDALONE)
+ .Authorization(km::TAG_NO_AUTH_REQUIRED)
+ .Authorization(km::TAG_MIN_SECONDS_BETWEEN_OPS, ratelimit);
+}
+
+/*
+int keymaster_create_key_for_cryptfs_scrypt(uint32_t rsa_key_size, uint64_t rsa_exponent,
+ uint32_t ratelimit, uint8_t* key_buffer,
+ uint32_t key_buffer_size, uint32_t* key_out_size) {
+ if (key_out_size) {
+ *key_out_size = 0;
+ }
+ Keymaster dev;
+ if (!dev) {
+ LOG(ERROR) << "Failed to initiate keymaster session" << std::endl;
+ return -1;
+ }
+ std::string key;
+ if (!dev.generateKey(keyParams(rsa_key_size, rsa_exponent, ratelimit), &key)) return -1;
+ if (!write_string_to_buf(key, key_buffer, key_buffer_size, key_out_size)) return -1;
+ return 0;
+}
+*/
+
+int keymaster_upgrade_key_for_cryptfs_scrypt(uint32_t rsa_key_size, uint64_t rsa_exponent,
+ uint32_t ratelimit, const uint8_t* key_blob,
+ size_t key_blob_size, uint8_t* key_buffer,
+ uint32_t key_buffer_size, uint32_t* key_out_size) {
+ if (key_out_size) {
+ *key_out_size = 0;
+ }
+ Keymaster dev;
+ if (!dev) {
+ LOG(ERROR) << "Failed to initiate keymaster session" << std::endl;
+ return -1;
+ }
+ std::string old_key(reinterpret_cast<const char*>(key_blob), key_blob_size);
+ std::string new_key;
+ if (!dev.upgradeKey(old_key, keyParams(rsa_key_size, rsa_exponent, ratelimit), &new_key))
+ return -1;
+ if (!write_string_to_buf(new_key, key_buffer, key_buffer_size, key_out_size)) return -1;
+ return 0;
+}
+
+KeymasterSignResult keymaster_sign_object_for_cryptfs_scrypt(
+ const uint8_t* key_blob, size_t key_blob_size, uint32_t ratelimit, const uint8_t* object,
+ const size_t object_size, uint8_t** signature_buffer, size_t* signature_buffer_size) {
+ Keymaster dev;
+ if (!dev) {
+ LOG(ERROR) << "Failed to initiate keymaster session" << std::endl;
+ return KeymasterSignResult::error;
+ }
+ if (!key_blob || !object || !signature_buffer || !signature_buffer_size) {
+ LOG(ERROR) << __FILE__ << ":" << __LINE__ << ":Invalid argument" << std::endl;
+ return KeymasterSignResult::error;
+ }
+
+ km::AuthorizationSet outParams;
+ std::string key(reinterpret_cast<const char*>(key_blob), key_blob_size);
+ std::string input(reinterpret_cast<const char*>(object), object_size);
+ std::string output;
+ KeymasterOperation op;
+
+ auto paramBuilder = km::AuthorizationSetBuilder().NoDigestOrPadding();
+ while (true) {
+ op = dev.begin(km::KeyPurpose::SIGN, key, paramBuilder, km::HardwareAuthToken(), &outParams);
+ if (op.errorCode() == km::ErrorCode::KEY_RATE_LIMIT_EXCEEDED) {
+ sleep(ratelimit);
+ continue;
+ } else
+ break;
+ }
+
+ if (op.errorCode() == km::ErrorCode::KEY_REQUIRES_UPGRADE) {
+ LOG(ERROR) << "Keymaster key requires upgrade" << std::endl;
+ return KeymasterSignResult::upgrade;
+ }
+
+ if (op.errorCode() != km::ErrorCode::OK) {
+ LOG(ERROR) << "Error starting keymaster signature transaction: " << int32_t(op.errorCode()) << std::endl;
+ return KeymasterSignResult::error;
+ }
+
+ if (!op.updateCompletely(input, &output)) {
+ LOG(ERROR) << "Error sending data to keymaster signature transaction: "
+ << uint32_t(op.errorCode()) << std::endl;
+ return KeymasterSignResult::error;
+ }
+
+ if (!op.finish(&output)) {
+ LOG(ERROR) << "Error finalizing keymaster signature transaction: "
+ << int32_t(op.errorCode()) << std::endl;
+ return KeymasterSignResult::error;
+ }
+
+ *signature_buffer = reinterpret_cast<uint8_t*>(malloc(output.size()));
+ if (*signature_buffer == nullptr) {
+ LOG(ERROR) << "Error allocation buffer for keymaster signature" << std::endl;
+ return KeymasterSignResult::error;
+ }
+ *signature_buffer_size = output.size();
+ std::copy(output.data(), output.data() + output.size(), *signature_buffer);
+ return KeymasterSignResult::ok;
+}
diff --git a/crypto/ext4crypt/Keymaster4.h b/crypto/ext4crypt/Keymaster4.h
new file mode 100644
index 0000000..704cc2c
--- /dev/null
+++ b/crypto/ext4crypt/Keymaster4.h
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_TWRP_KEYMASTER_H
+#define ANDROID_TWRP_KEYMASTER_H
+
+#include "KeyBuffer.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include <android-base/macros.h>
+#include <keymasterV4_0/Keymaster.h>
+#include <keymasterV4_0/authorization_set.h>
+
+namespace android {
+namespace vold {
+
+namespace km = ::android::hardware::keymaster::V4_0;
+using KmDevice = km::support::Keymaster;
+
+// C++ wrappers to the Keymaster hidl interface.
+// This is tailored to the needs of KeyStorage, but could be extended to be
+// a more general interface.
+
+// Wrapper for a Keymaster operation handle representing an
+// ongoing Keymaster operation. Aborts the operation
+// in the destructor if it is unfinished. Methods log failures
+// to LOG(ERROR).
+class KeymasterOperation {
+ public:
+ ~KeymasterOperation();
+ // Is this instance valid? This is false if creation fails, and becomes
+ // false on finish or if an update fails.
+ explicit operator bool() { return mError == km::ErrorCode::OK; }
+ km::ErrorCode errorCode() { return mError; }
+ // Call "update" repeatedly until all of the input is consumed, and
+ // concatenate the output. Return true on success.
+ template <class TI, class TO>
+ bool updateCompletely(TI& input, TO* output) {
+ if (output) output->clear();
+ return updateCompletely(input.data(), input.size(), [&](const char* b, size_t n) {
+ if (output) std::copy(b, b + n, std::back_inserter(*output));
+ });
+ }
+
+ // Finish and write the output to this string, unless pointer is null.
+ bool finish(std::string* output);
+ // Move constructor
+ KeymasterOperation(KeymasterOperation&& rhs) { *this = std::move(rhs); }
+ // Construct an object in an error state for error returns
+ KeymasterOperation() : mDevice{nullptr}, mOpHandle{0}, mError{km::ErrorCode::UNKNOWN_ERROR} {}
+ // Move Assignment
+ KeymasterOperation& operator=(KeymasterOperation&& rhs) {
+ mDevice = rhs.mDevice;
+ rhs.mDevice = nullptr;
+
+ mOpHandle = rhs.mOpHandle;
+ rhs.mOpHandle = 0;
+
+ mError = rhs.mError;
+ rhs.mError = km::ErrorCode::UNKNOWN_ERROR;
+
+ return *this;
+ }
+
+ private:
+ KeymasterOperation(KmDevice* d, uint64_t h)
+ : mDevice{d}, mOpHandle{h}, mError{km::ErrorCode::OK} {}
+ KeymasterOperation(km::ErrorCode error) : mDevice{nullptr}, mOpHandle{0}, mError{error} {}
+
+ bool updateCompletely(const char* input, size_t inputLen,
+ const std::function<void(const char*, size_t)> consumer);
+
+ KmDevice* mDevice;
+ uint64_t mOpHandle;
+ km::ErrorCode mError;
+ DISALLOW_COPY_AND_ASSIGN(KeymasterOperation);
+ friend class Keymaster;
+};
+
+// Wrapper for a Keymaster device for methods that start a KeymasterOperation or are not
+// part of one.
+class Keymaster {
+ public:
+ Keymaster();
+ // false if we failed to open the keymaster device.
+ explicit operator bool() { return mDevice.get() != nullptr; }
+ // Generate a key in the keymaster from the given params.
+ bool generateKey(const km::AuthorizationSet& inParams, std::string* key);
+ // Export a key from keymaster.
+ km::ErrorCode exportKey(km::KeyFormat format, KeyBuffer& kmKey, const std::string& clientId,
+ const std::string& appData, std::string* key);
+ // If the keymaster supports it, permanently delete a key.
+ bool deleteKey(const std::string& key);
+ // Replace stored key blob in response to KM_ERROR_KEY_REQUIRES_UPGRADE.
+ bool upgradeKey(const std::string& oldKey, const km::AuthorizationSet& inParams,
+ std::string* newKey);
+ // Begin a new cryptographic operation, collecting output parameters if pointer is non-null
+ KeymasterOperation begin(km::KeyPurpose purpose, const std::string& key,
+ const km::AuthorizationSet& inParams,
+ const km::HardwareAuthToken& authToken,
+ km::AuthorizationSet* outParams);
+ bool isSecure();
+
+ private:
+ std::unique_ptr<KmDevice> mDevice;
+ DISALLOW_COPY_AND_ASSIGN(Keymaster);
+ static bool hmacKeyGenerated;
+};
+
+} // namespace vold
+} // namespace android
+
+// FIXME no longer needed now cryptfs is in C++.
+
+/*
+ * The following functions provide C bindings to keymaster services
+ * needed by cryptfs scrypt. The compatibility check checks whether
+ * the keymaster implementation is considered secure, i.e., TEE backed.
+ * The create_key function generates an RSA key for signing.
+ * The sign_object function signes an object with the given keymaster
+ * key.
+ */
+
+/* Return values for keymaster_sign_object_for_cryptfs_scrypt */
+
+enum class KeymasterSignResult {
+ ok = 0,
+ error = -1,
+ upgrade = -2,
+};
+
+//int keymaster_compatibility_cryptfs_scrypt();
+/*int keymaster_create_key_for_cryptfs_scrypt(uint32_t rsa_key_size, uint64_t rsa_exponent,
+ uint32_t ratelimit, uint8_t* key_buffer,
+ uint32_t key_buffer_size, uint32_t* key_out_size);*/
+
+int keymaster_upgrade_key_for_cryptfs_scrypt(uint32_t rsa_key_size, uint64_t rsa_exponent,
+ uint32_t ratelimit, const uint8_t* key_blob,
+ size_t key_blob_size, uint8_t* key_buffer,
+ uint32_t key_buffer_size, uint32_t* key_out_size);
+
+KeymasterSignResult keymaster_sign_object_for_cryptfs_scrypt(
+ const uint8_t* key_blob, size_t key_blob_size, uint32_t ratelimit, const uint8_t* object,
+ const size_t object_size, uint8_t** signature_buffer, size_t* signature_buffer_size);
+
+#endif
diff --git a/crypto/ext4crypt/MetadataCrypt.cpp b/crypto/ext4crypt/MetadataCrypt.cpp
new file mode 100644
index 0000000..b195fa0
--- /dev/null
+++ b/crypto/ext4crypt/MetadataCrypt.cpp
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "KeyBuffer.h"
+#include "MetadataCrypt.h"
+
+#include <string>
+#include <thread>
+#include <vector>
+#include <algorithm>
+
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <linux/dm-ioctl.h>
+
+//#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/unique_fd.h>
+#include <cutils/fs.h>
+//#include <fs_mgr.h>
+
+//#include "EncryptInplace.h"
+#include "KeyStorage4.h"
+#include "KeyUtil.h"
+//#include "secontext.h"
+#include "Utils.h"
+//#include "VoldUtil.h"
+
+#include <iostream>
+#define LOG(x) std::cout
+#define PLOG(x) std::cout
+#include <linux/fs.h>
+
+#define DM_CRYPT_BUF_SIZE 4096
+#define TABLE_LOAD_RETRIES 10
+#define DEFAULT_KEY_TARGET_TYPE "default-key"
+
+using android::vold::KeyBuffer;
+
+static const std::string kDmNameUserdata = "userdata";
+
+void get_blkdev_size(int fd, unsigned long* nr_sec) {
+ if ((ioctl(fd, BLKGETSIZE, nr_sec)) == -1) {
+ *nr_sec = 0;
+ }
+}
+
+static const char* kLookup = "0123456789abcdef";
+
+android::status_t StrToHex(const KeyBuffer& str, KeyBuffer& hex) {
+ hex.clear();
+ for (size_t i = 0; i < str.size(); i++) {
+ hex.push_back(kLookup[(str.data()[i] & 0xF0) >> 4]);
+ hex.push_back(kLookup[str.data()[i] & 0x0F]);
+ }
+ return android::OK;
+}
+
+/*static bool mount_via_fs_mgr(const char* mount_point, const char* blk_device) {
+ // fs_mgr_do_mount runs fsck. Use setexeccon to run trusted
+ // partitions in the fsck domain.
+ if (setexeccon(secontextFsck())) {
+ PLOG(ERROR) << "Failed to setexeccon";
+ return false;
+ }
+ auto mount_rc = fs_mgr_do_mount(fstab_default, const_cast<char*>(mount_point),
+ const_cast<char*>(blk_device), nullptr);
+ if (setexeccon(nullptr)) {
+ PLOG(ERROR) << "Failed to clear setexeccon";
+ return false;
+ }
+ if (mount_rc != 0) {
+ LOG(ERROR) << "fs_mgr_do_mount failed with rc " << mount_rc;
+ return false;
+ }
+ LOG(DEBUG) << "Mounted " << mount_point;
+ return true;
+}*/
+
+static bool read_key(const std::string& key_dir, bool create_if_absent, KeyBuffer* key) {
+ /*if (!data_rec->key_dir) {
+ LOG(ERROR) << "Failed to get key_dir";
+ return false;
+ }
+ std::string key_dir = data_rec->key_dir;*/
+ auto dir = key_dir + "/key";
+ LOG(DEBUG) << "key_dir/key: " << dir << "\n";
+ /*if (fs_mkdirs(dir.c_str(), 0700)) {
+ PLOG(ERROR) << "Creating directories: " << dir;
+ return false;
+ }*/
+ auto temp = key_dir + "/tmp";
+ if (!android::vold::retrieveKey(create_if_absent, dir, temp, key)) return false;
+ return true;
+}
+
+static KeyBuffer default_key_params(const std::string& real_blkdev, const KeyBuffer& key) {
+ KeyBuffer hex_key;
+ if (/*android::vold::*/StrToHex(key, hex_key) != android::OK) {
+ LOG(ERROR) << "Failed to turn key to hex\n";
+ return KeyBuffer();
+ }
+ auto res = KeyBuffer() + "AES-256-XTS " + hex_key + " " + real_blkdev.c_str() + " 0";
+ return res;
+}
+
+static bool get_number_of_sectors(const std::string& real_blkdev, uint64_t *nr_sec) {
+ android::base::unique_fd dev_fd(TEMP_FAILURE_RETRY(open(
+ real_blkdev.c_str(), O_RDONLY | O_CLOEXEC, 0)));
+ if (dev_fd == -1) {
+ PLOG(ERROR) << "Unable to open " << real_blkdev << " to measure size\n";
+ return false;
+ }
+ unsigned long res;
+ // TODO: should use BLKGETSIZE64
+ get_blkdev_size(dev_fd.get(), &res);
+ if (res == 0) {
+ PLOG(ERROR) << "Unable to measure size of " << real_blkdev << "\n";
+ return false;
+ }
+ *nr_sec = res;
+ return true;
+}
+
+static struct dm_ioctl* dm_ioctl_init(char *buffer, size_t buffer_size,
+ const std::string& dm_name) {
+ if (buffer_size < sizeof(dm_ioctl)) {
+ LOG(ERROR) << "dm_ioctl buffer too small\n";
+ return nullptr;
+ }
+
+ memset(buffer, 0, buffer_size);
+ struct dm_ioctl* io = (struct dm_ioctl*) buffer;
+ io->data_size = buffer_size;
+ io->data_start = sizeof(struct dm_ioctl);
+ io->version[0] = 4;
+ io->version[1] = 0;
+ io->version[2] = 0;
+ io->flags = 0;
+ dm_name.copy(io->name, sizeof(io->name));
+ return io;
+}
+
+static bool create_crypto_blk_dev(const std::string& dm_name, uint64_t nr_sec,
+ const std::string& target_type, const KeyBuffer& crypt_params,
+ std::string* crypto_blkdev) {
+ PLOG(INFO) << "starting create_crypto_blk_dev\n";
+ android::base::unique_fd dm_fd(TEMP_FAILURE_RETRY(open(
+ "/dev/device-mapper", O_RDWR | O_CLOEXEC, 0)));
+ if (dm_fd == -1) {
+ PLOG(ERROR) << "Cannot open device-mapper\n";
+ return false;
+ }
+ alignas(struct dm_ioctl) char buffer[DM_CRYPT_BUF_SIZE];
+ auto io = dm_ioctl_init(buffer, sizeof(buffer), dm_name);
+ if (!io || ioctl(dm_fd.get(), DM_DEV_CREATE, io) != 0) {
+ PLOG(ERROR) << "Cannot create dm-crypt device " << dm_name << "\n";
+ return false;
+ }
+
+ // Get the device status, in particular, the name of its device file
+ io = dm_ioctl_init(buffer, sizeof(buffer), dm_name);
+ if (ioctl(dm_fd.get(), DM_DEV_STATUS, io) != 0) {
+ PLOG(ERROR) << "Cannot retrieve dm-crypt device status " << dm_name << "\n";
+ return false;
+ }
+ *crypto_blkdev = std::string() + "/dev/block/dm-" + std::to_string(
+ (io->dev & 0xff) | ((io->dev >> 12) & 0xfff00));
+
+ io = dm_ioctl_init(buffer, sizeof(buffer), dm_name);
+ size_t paramix = io->data_start + sizeof(struct dm_target_spec);
+ size_t nullix = paramix + crypt_params.size();
+ size_t endix = (nullix + 1 + 7) & 8; // Add room for \0 and align to 8 byte boundary
+
+ if (endix > sizeof(buffer)) {
+ LOG(ERROR) << "crypt_params too big for DM_CRYPT_BUF_SIZE\n";
+ return false;
+ }
+
+ io->target_count = 1;
+ auto tgt = (struct dm_target_spec *) (buffer + io->data_start);
+ tgt->status = 0;
+ tgt->sector_start = 0;
+ tgt->length = nr_sec;
+ target_type.copy(tgt->target_type, sizeof(tgt->target_type));
+ memcpy(buffer + paramix, crypt_params.data(),
+ std::min(crypt_params.size(), sizeof(buffer) - paramix));
+ buffer[nullix] = '\0';
+ tgt->next = endix;
+
+ for (int i = 0; ; i++) {
+ if (ioctl(dm_fd.get(), DM_TABLE_LOAD, io) == 0) {
+ break;
+ }
+ if (i+1 >= TABLE_LOAD_RETRIES) {
+ PLOG(ERROR) << "DM_TABLE_LOAD ioctl failed\n";
+ return false;
+ }
+ PLOG(INFO) << "DM_TABLE_LOAD ioctl failed, retrying\n";
+ usleep(500000);
+ }
+
+ // Resume this device to activate it
+ io = dm_ioctl_init(buffer, sizeof(buffer), dm_name);
+ if (ioctl(dm_fd.get(), DM_DEV_SUSPEND, io)) {
+ PLOG(ERROR) << "Cannot resume dm-crypt device " << dm_name << "\n";
+ return false;
+ }
+ return true;
+}
+
+bool e4crypt_mount_metadata_encrypted(const std::string& mount_point, bool needs_encrypt, const std::string& key_dir, const std::string& blk_device, std::string* crypto_blkdev) {
+ LOG(DEBUG) << "e4crypt_mount_metadata_encrypted: " << mount_point << " " << needs_encrypt << "\n";
+ /*auto encrypted_state = android::base::GetProperty("ro.crypto.state", "");
+ if (encrypted_state != "") {
+ LOG(DEBUG) << "e4crypt_enable_crypto got unexpected starting state: " << encrypted_state;
+ return false;
+ }
+ auto data_rec = fs_mgr_get_entry_for_mount_point(fstab_default, mount_point);
+ if (!data_rec) {
+ LOG(ERROR) << "Failed to get data_rec";
+ return false;
+ }*/
+ KeyBuffer key;
+ if (!read_key(key_dir, needs_encrypt, &key)) return false;
+ uint64_t nr_sec;
+ if (!get_number_of_sectors(blk_device, &nr_sec)) return false;
+ //std::string crypto_blkdev;
+ if (!create_crypto_blk_dev(kDmNameUserdata, nr_sec, DEFAULT_KEY_TARGET_TYPE,
+ default_key_params(blk_device, key), /*&*/crypto_blkdev))
+ return false;
+ // FIXME handle the corrupt case
+ /*if (needs_encrypt) {
+ LOG(INFO) << "Beginning inplace encryption, nr_sec: " << nr_sec;
+ off64_t size_already_done = 0;
+ auto rc =
+ cryptfs_enable_inplace(const_cast<char*>(crypto_blkdev.c_str()), data_rec->blk_device,
+ nr_sec, &size_already_done, nr_sec, 0, false);
+ if (rc != 0) {
+ LOG(ERROR) << "Inplace crypto failed with code: " << rc;
+ return false;
+ }
+ if (static_cast<uint64_t>(size_already_done) != nr_sec) {
+ LOG(ERROR) << "Inplace crypto only got up to sector: " << size_already_done;
+ return false;
+ }
+ LOG(INFO) << "Inplace encryption complete";
+ }
+
+ LOG(DEBUG) << "Mounting metadata-encrypted filesystem:" << mount_point;
+ mount_via_fs_mgr(data_rec->mount_point, crypto_blkdev.c_str());*/
+ LOG(DEBUG) << "crypto block device '" << *crypto_blkdev << "\n";
+ return true;
+}
diff --git a/crypto/ext4crypt/MetadataCrypt.h b/crypto/ext4crypt/MetadataCrypt.h
new file mode 100644
index 0000000..69addf3
--- /dev/null
+++ b/crypto/ext4crypt/MetadataCrypt.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _METADATA_CRYPT_H
+#define _METADATA_CRYPT_H
+
+#include <string>
+__BEGIN_DECLS
+bool e4crypt_mount_metadata_encrypted(const std::string& mount_point, bool needs_encrypt, const std::string& key_dir, const std::string& blk_device, std::string* crypto_blkdev);
+__END_DECLS
+
+#endif
diff --git a/crypto/ext4crypt/ScryptParameters.cpp b/crypto/ext4crypt/ScryptParameters.cpp
new file mode 100644
index 0000000..669809b
--- /dev/null
+++ b/crypto/ext4crypt/ScryptParameters.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ScryptParameters.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+bool parse_scrypt_parameters(const char* paramstr, int *Nf, int *rf, int *pf) {
+ int params[3];
+ char *token;
+ char *saveptr;
+ int i;
+
+ /*
+ * The token we're looking for should be three integers separated by
+ * colons (e.g., "12:8:1"). Scan the property to make sure it matches.
+ */
+ for (i = 0, token = strtok_r(const_cast<char *>(paramstr), ":", &saveptr);
+ token != nullptr && i < 3;
+ i++, token = strtok_r(nullptr, ":", &saveptr)) {
+ char *endptr;
+ params[i] = strtol(token, &endptr, 10);
+
+ /*
+ * Check that there was a valid number and it's 8-bit.
+ */
+ if ((*token == '\0') || (*endptr != '\0') || params[i] < 0 || params[i] > 255) {
+ return false;
+ }
+ }
+ if (token != nullptr) {
+ return false;
+ }
+ *Nf = params[0]; *rf = params[1]; *pf = params[2];
+ return true;
+}
diff --git a/crypto/ext4crypt/ScryptParameters.h b/crypto/ext4crypt/ScryptParameters.h
new file mode 100644
index 0000000..1b43ea5
--- /dev/null
+++ b/crypto/ext4crypt/ScryptParameters.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_VOLD_SCRYPT_PARAMETERS_H
+#define ANDROID_VOLD_SCRYPT_PARAMETERS_H
+
+#include <stdbool.h>
+#include <sys/cdefs.h>
+
+#define SCRYPT_PROP "ro.crypto.scrypt_params"
+#define SCRYPT_DEFAULTS "15:3:1"
+
+__BEGIN_DECLS
+
+bool parse_scrypt_parameters(const char* paramstr, int *Nf, int *rf, int *pf);
+
+__END_DECLS
+
+#endif
diff --git a/crypto/ext4crypt/Utils.cpp b/crypto/ext4crypt/Utils.cpp
new file mode 100644
index 0000000..9711cef
--- /dev/null
+++ b/crypto/ext4crypt/Utils.cpp
@@ -0,0 +1,307 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Utils.h"
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+
+#include <fcntl.h>
+#include <linux/fs.h>
+#include <stdlib.h>
+#include <sys/mount.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/statvfs.h>
+
+#include <selinux/android.h>
+
+using android::base::ReadFileToString;
+using android::base::StringPrintf;
+
+namespace android {
+namespace vold {
+
+static const char* kKeyPath = "/data/misc/vold";
+
+status_t ForkExecvp(const std::vector<std::string>& args) {
+ return ForkExecvp(args, nullptr);
+}
+
+status_t ForkExecvp(const std::vector<std::string>& args, security_context_t context) {
+ size_t argc = args.size();
+ char** argv = (char**) calloc(argc, sizeof(char*));
+ for (size_t i = 0; i < argc; i++) {
+ argv[i] = (char*) args[i].c_str();
+ if (i == 0) {
+ LOG(VERBOSE) << args[i];
+ } else {
+ LOG(VERBOSE) << " " << args[i];
+ }
+ }
+
+ if (setexeccon(context)) {
+ LOG(ERROR) << "Failed to setexeccon" << std::endl;
+ abort();
+ }
+ abort();
+ status_t res = 1;//android_fork_execvp(argc, argv, NULL, false, true);
+ if (setexeccon(nullptr)) {
+ LOG(ERROR) << "Failed to setexeccon" << std::endl;
+ abort();
+ }
+
+ free(argv);
+ return res;
+}
+
+status_t ForkExecvp(const std::vector<std::string>& args,
+ std::vector<std::string>& output) {
+ return ForkExecvp(args, output, nullptr);
+}
+
+status_t ForkExecvp(const std::vector<std::string>& args,
+ std::vector<std::string>& output, security_context_t context) {
+ std::string cmd;
+ for (size_t i = 0; i < args.size(); i++) {
+ cmd += args[i] + " ";
+ if (i == 0) {
+ LOG(VERBOSE) << args[i];
+ } else {
+ LOG(VERBOSE) << " " << args[i];
+ }
+ }
+ output.clear();
+
+ if (setexeccon(context)) {
+ LOG(ERROR) << "Failed to setexeccon" << std::endl;
+ abort();
+ }
+ FILE* fp = popen(cmd.c_str(), "r");
+ if (setexeccon(nullptr)) {
+ LOG(ERROR) << "Failed to setexeccon" << std::endl;
+ abort();
+ }
+
+ if (!fp) {
+ PLOG(ERROR) << "Failed to popen " << cmd << std::endl;
+ return -errno;
+ }
+ char line[1024];
+ while (fgets(line, sizeof(line), fp) != nullptr) {
+ LOG(VERBOSE) << line;
+ output.push_back(std::string(line));
+ }
+ if (pclose(fp) != 0) {
+ PLOG(ERROR) << "Failed to pclose " << cmd << std::endl;
+ return -errno;
+ }
+
+ return OK;
+}
+
+pid_t ForkExecvpAsync(const std::vector<std::string>& args) {
+ size_t argc = args.size();
+ char** argv = (char**) calloc(argc + 1, sizeof(char*));
+ for (size_t i = 0; i < argc; i++) {
+ argv[i] = (char*) args[i].c_str();
+ if (i == 0) {
+ LOG(VERBOSE) << args[i];
+ } else {
+ LOG(VERBOSE) << " " << args[i];
+ }
+ }
+
+ pid_t pid = fork();
+ if (pid == 0) {
+ close(STDIN_FILENO);
+ close(STDOUT_FILENO);
+ close(STDERR_FILENO);
+
+ if (execvp(argv[0], argv)) {
+ PLOG(ERROR) << "Failed to exec" << std::endl;
+ }
+
+ _exit(1);
+ }
+
+ if (pid == -1) {
+ PLOG(ERROR) << "Failed to exec" << std::endl;
+ }
+
+ free(argv);
+ return pid;
+}
+
+status_t ReadRandomBytes(size_t bytes, std::string& out) {
+ out.resize(bytes);
+ return ReadRandomBytes(bytes, &out[0]);
+}
+
+status_t ReadRandomBytes(size_t bytes, char* buf) {
+ int fd = TEMP_FAILURE_RETRY(open("/dev/urandom", O_RDONLY | O_CLOEXEC | O_NOFOLLOW));
+ if (fd == -1) {
+ return -errno;
+ }
+
+ size_t n;
+ while ((n = TEMP_FAILURE_RETRY(read(fd, &buf[0], bytes))) > 0) {
+ bytes -= n;
+ buf += n;
+ }
+ close(fd);
+
+ if (bytes == 0) {
+ return OK;
+ } else {
+ return -EIO;
+ }
+}
+
+status_t HexToStr(const std::string& hex, std::string& str) {
+ str.clear();
+ bool even = true;
+ char cur = 0;
+ for (size_t i = 0; i < hex.size(); i++) {
+ int val = 0;
+ switch (hex[i]) {
+ case ' ': case '-': case ':': continue;
+ case 'f': case 'F': val = 15; break;
+ case 'e': case 'E': val = 14; break;
+ case 'd': case 'D': val = 13; break;
+ case 'c': case 'C': val = 12; break;
+ case 'b': case 'B': val = 11; break;
+ case 'a': case 'A': val = 10; break;
+ case '9': val = 9; break;
+ case '8': val = 8; break;
+ case '7': val = 7; break;
+ case '6': val = 6; break;
+ case '5': val = 5; break;
+ case '4': val = 4; break;
+ case '3': val = 3; break;
+ case '2': val = 2; break;
+ case '1': val = 1; break;
+ case '0': val = 0; break;
+ default: return -EINVAL;
+ }
+
+ if (even) {
+ cur = val << 4;
+ } else {
+ cur += val;
+ str.push_back(cur);
+ cur = 0;
+ }
+ even = !even;
+ }
+ return even ? OK : -EINVAL;
+}
+
+static bool isValidFilename(const std::string& name) {
+ if (name.empty() || (name == ".") || (name == "..")
+ || (name.find('/') != std::string::npos)) {
+ return false;
+ } else {
+ return true;
+ }
+}
+
+std::string BuildKeyPath(const std::string& partGuid) {
+ return StringPrintf("%s/expand_%s.key", kKeyPath, partGuid.c_str());
+}
+
+std::string BuildDataSystemLegacyPath(userid_t userId) {
+ return StringPrintf("%s/system/users/%u", BuildDataPath(nullptr).c_str(), userId);
+}
+
+std::string BuildDataSystemCePath(userid_t userId) {
+ return StringPrintf("%s/system_ce/%u", BuildDataPath(nullptr).c_str(), userId);
+}
+
+std::string BuildDataSystemDePath(userid_t userId) {
+ return StringPrintf("%s/system_de/%u", BuildDataPath(nullptr).c_str(), userId);
+}
+
+std::string BuildDataMiscLegacyPath(userid_t userId) {
+ return StringPrintf("%s/misc/user/%u", BuildDataPath(nullptr).c_str(), userId);
+}
+
+std::string BuildDataMiscCePath(userid_t userId) {
+ return StringPrintf("%s/misc_ce/%u", BuildDataPath(nullptr).c_str(), userId);
+}
+
+std::string BuildDataMiscDePath(userid_t userId) {
+ return StringPrintf("%s/misc_de/%u", BuildDataPath(nullptr).c_str(), userId);
+}
+
+std::string BuildDataVendorCePath(userid_t userId) {
+ return StringPrintf("%s/vendor_ce/%u", BuildDataPath(nullptr).c_str(), userId);
+}
+
+std::string BuildDataVendorDePath(userid_t userId) {
+ return StringPrintf("%s/vendor_de/%u", BuildDataPath(nullptr).c_str(), userId);
+}
+
+// Keep in sync with installd (frameworks/native/cmds/installd/utils.h)
+std::string BuildDataProfilesDePath(userid_t userId) {
+ return StringPrintf("%s/misc/profiles/cur/%u", BuildDataPath(nullptr).c_str(), userId);
+}
+
+std::string BuildDataProfilesForeignDexDePath(userid_t userId) {
+ std::string profiles_path = BuildDataProfilesDePath(userId);
+ return StringPrintf("%s/foreign-dex", profiles_path.c_str());
+}
+
+std::string BuildDataPath(const char* volumeUuid) {
+ // TODO: unify with installd path generation logic
+ if (volumeUuid == nullptr) {
+ return "/data";
+ } else {
+ CHECK(isValidFilename(volumeUuid));
+ return StringPrintf("/mnt/expand/%s", volumeUuid);
+ }
+}
+
+std::string BuildDataMediaCePath(const char* volumeUuid, userid_t userId) {
+ // TODO: unify with installd path generation logic
+ std::string data(BuildDataPath(volumeUuid));
+ return StringPrintf("%s/media/%u", data.c_str(), userId);
+}
+
+std::string BuildDataUserCePath(const char* volumeUuid, userid_t userId) {
+ // TODO: unify with installd path generation logic
+ std::string data(BuildDataPath(volumeUuid));
+ if (volumeUuid == nullptr) {
+ if (userId == 0) {
+ return StringPrintf("%s/data", data.c_str());
+ } else {
+ return StringPrintf("%s/user/%u", data.c_str(), userId);
+ }
+ } else {
+ return StringPrintf("%s/user/%u", data.c_str(), userId);
+ }
+}
+
+std::string BuildDataUserDePath(const char* volumeUuid, userid_t userId) {
+ // TODO: unify with installd path generation logic
+ std::string data(BuildDataPath(volumeUuid));
+ return StringPrintf("%s/user_de/%u", data.c_str(), userId);
+}
+
+} // namespace vold
+} // namespace android
diff --git a/crypto/ext4crypt/Utils.h b/crypto/ext4crypt/Utils.h
new file mode 100644
index 0000000..253541c
--- /dev/null
+++ b/crypto/ext4crypt/Utils.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TWRP_VOLD_UTILS_H
+#define TWRP_VOLD_UTILS_H
+
+#include <utils/Errors.h>
+#include <cutils/multiuser.h>
+#include <selinux/selinux.h>
+
+#include <vector>
+#include <string>
+
+// DISALLOW_COPY_AND_ASSIGN disallows the copy and operator= functions. It goes in the private:
+// declarations in a class.
+#if !defined(DISALLOW_COPY_AND_ASSIGN)
+#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
+ TypeName(const TypeName&) = delete; \
+ void operator=(const TypeName&) = delete
+#endif
+
+namespace android {
+namespace vold {
+
+/* Returns either WEXITSTATUS() status, or a negative errno */
+status_t ForkExecvp(const std::vector<std::string>& args);
+status_t ForkExecvp(const std::vector<std::string>& args, security_context_t context);
+
+status_t ForkExecvp(const std::vector<std::string>& args,
+ std::vector<std::string>& output);
+status_t ForkExecvp(const std::vector<std::string>& args,
+ std::vector<std::string>& output, security_context_t context);
+
+pid_t ForkExecvpAsync(const std::vector<std::string>& args);
+
+status_t ReadRandomBytes(size_t bytes, std::string& out);
+status_t ReadRandomBytes(size_t bytes, char* buffer);
+
+/* Converts hex string to raw bytes, ignoring [ :-] */
+status_t HexToStr(const std::string& hex, std::string& str);
+
+std::string BuildKeyPath(const std::string& partGuid);
+
+std::string BuildDataSystemLegacyPath(userid_t userid);
+std::string BuildDataSystemCePath(userid_t userid);
+std::string BuildDataSystemDePath(userid_t userid);
+std::string BuildDataMiscLegacyPath(userid_t userid);
+std::string BuildDataMiscCePath(userid_t userid);
+std::string BuildDataMiscDePath(userid_t userid);
+std::string BuildDataProfilesDePath(userid_t userid);
+std::string BuildDataProfilesForeignDexDePath(userid_t userid);
+std::string BuildDataVendorCePath(userid_t userid);
+std::string BuildDataVendorDePath(userid_t userid);
+
+std::string BuildDataPath(const char* volumeUuid);
+std::string BuildDataMediaCePath(const char* volumeUuid, userid_t userid);
+std::string BuildDataUserCePath(const char* volumeUuid, userid_t userid);
+std::string BuildDataUserDePath(const char* volumeUuid, userid_t userid);
+
+} // namespace vold
+} // namespace android
+
+#endif
diff --git a/crypto/ext4crypt/Weaver1.cpp b/crypto/ext4crypt/Weaver1.cpp
new file mode 100644
index 0000000..ea357ed
--- /dev/null
+++ b/crypto/ext4crypt/Weaver1.cpp
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2017 Team Win Recovery Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* To the best of my knowledge there is no native implementation for
+ * Weaver so I made this by looking at the IWeaver.h file that gets
+ * compiled by the build system. I took the information from this header
+ * file and looked at keymaster source to get an idea of the proper way
+ * to write the functions.
+ */
+
+#include "Weaver1.h"
+
+//#include <android-base/logging.h>
+//#include <keystore/keymaster_tags.h>
+//#include <keystore/authorization_set.h>
+//#include <keystore/keystore_hidl_support.h>
+
+#include <android/hardware/weaver/1.0/IWeaver.h>
+
+#include <iostream>
+#define ERROR 1
+#define LOG(x) std::cout
+
+using namespace android::hardware::weaver;
+using android::hardware::hidl_string;
+using ::android::hardware::weaver::V1_0::IWeaver;
+using ::android::hardware::weaver::V1_0::WeaverConfig;
+using ::android::hardware::weaver::V1_0::WeaverReadStatus;
+using ::android::hardware::weaver::V1_0::WeaverReadResponse;
+using ::android::hardware::weaver::V1_0::WeaverStatus;
+using ::android::hardware::Return;
+using ::android::sp;
+
+namespace android {
+namespace vold {
+
+Weaver::Weaver() {
+ mDevice = ::android::hardware::weaver::V1_0::IWeaver::getService();
+ GottenConfig = false;
+}
+
+bool Weaver::GetConfig() {
+ if (GottenConfig)
+ return true;
+
+ WeaverStatus status;
+ WeaverConfig cfg;
+
+ bool callbackCalled = false;
+ auto ret = mDevice->getConfig([&](WeaverStatus s, WeaverConfig c) {
+ callbackCalled = true;
+ status = s;
+ cfg = c;
+ });
+ if (ret.isOk() && callbackCalled && status == WeaverStatus::OK) {
+ config = cfg;
+ GottenConfig = true;
+ return true;
+ }
+ return false;
+}
+
+bool Weaver::GetSlots(uint32_t* slots) {
+ if (!GetConfig())
+ return false;
+ *slots = config.slots;
+ return true;
+}
+
+bool Weaver::GetKeySize(uint32_t* keySize) {
+ if (!GetConfig())
+ return false;
+ *keySize = config.keySize;
+ return true;
+}
+
+bool Weaver::GetValueSize(uint32_t* valueSize) {
+ if (!GetConfig())
+ return false;
+ *valueSize = config.valueSize;
+ return true;
+}
+
+// TODO: we should return more information about the status including time delays before the next retry
+bool Weaver::WeaverVerify(const uint32_t slot, const void* weaver_key, std::vector<uint8_t>* payload) {
+ bool callbackCalled = false;
+ WeaverReadStatus status;
+ std::vector<uint8_t> readValue;
+ uint32_t timeout;
+ uint32_t keySize;
+ if (!GetKeySize(&keySize))
+ return false;
+ std::vector<uint8_t> key;
+ key.resize(keySize);
+ uint32_t index = 0;
+ unsigned char* ptr = (unsigned char*)weaver_key;
+ for (index = 0; index < keySize; index++) {
+ key[index] = *ptr;
+ ptr++;
+ }
+ const auto readRet = mDevice->read(slot, key, [&](WeaverReadStatus s, WeaverReadResponse r) {
+ callbackCalled = true;
+ status = s;
+ readValue = r.value;
+ timeout = r.timeout;
+ });
+ if (readRet.isOk() && callbackCalled && status == WeaverReadStatus::OK && timeout == 0) {
+ *payload = readValue;
+ return true;
+ }
+ return false;
+}
+
+} // namespace vold
+} // namespace android
diff --git a/crypto/ext4crypt/Weaver1.h b/crypto/ext4crypt/Weaver1.h
new file mode 100644
index 0000000..22f401e
--- /dev/null
+++ b/crypto/ext4crypt/Weaver1.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2017 Team Win Recovery Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* To the best of my knowledge there is no native implementation for
+ * Weaver so I made this by looking at the IWeaver.h file that gets
+ * compiled by the build system. I took the information from this header
+ * file and looked at keymaster source to get an idea of the proper way
+ * to write the functions.
+ */
+
+#ifndef TWRP_WEAVER_H
+#define TWRP_WEAVER_H
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include <android/hardware/weaver/1.0/IWeaver.h>
+#include "Utils.h"
+
+namespace android {
+namespace vold {
+using ::android::hardware::weaver::V1_0::IWeaver;
+
+// Wrapper for a Weaver device
+class Weaver {
+ public:
+ Weaver();
+ // false if we failed to open the weaver device.
+ explicit operator bool() { return mDevice.get() != nullptr; }
+
+ bool GetSlots(uint32_t* slots);
+ bool GetKeySize(uint32_t* keySize);
+ bool GetValueSize(uint32_t* valueSize);
+ // TODO: we should return more information about the status including time delays before the next retry
+ bool WeaverVerify(const uint32_t slot, const void* weaver_key, std::vector<uint8_t>* payload);
+
+ private:
+ sp<hardware::weaver::V1_0::IWeaver> mDevice;
+ hardware::weaver::V1_0::WeaverConfig config;
+ bool GottenConfig;
+
+ bool GetConfig();
+
+ DISALLOW_COPY_AND_ASSIGN(Weaver);
+};
+
+} // namespace vold
+} // namespace android
+
+#endif
diff --git a/crypto/ext4crypt/e4policyget.cpp b/crypto/ext4crypt/e4policyget.cpp
new file mode 100644
index 0000000..05de86f
--- /dev/null
+++ b/crypto/ext4crypt/e4policyget.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2016 Team Win Recovery Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "ext4crypt_tar.h"
+
+#define EXT4_KEY_DESCRIPTOR_SIZE 8
+#define EXT4_KEY_DESCRIPTOR_SIZE_HEX 17
+
+int main(int argc, char *argv[]) {
+ bool ret = false;
+ if (argc != 2) {
+ printf("Must specify a path\n");
+ return -1;
+ } else {
+ ext4_encryption_policy eep;
+ if (e4crypt_policy_get_struct(argv[1], &eep, sizeof(eep))) {
+ char policy_hex[EXT4_KEY_DESCRIPTOR_SIZE_HEX];
+ policy_to_hex(eep.master_key_descriptor, policy_hex);
+ printf("%s\n", policy_hex);
+ } else {
+ printf("No policy set\n");
+ }
+ }
+ return 0;
+}
diff --git a/crypto/ext4crypt/ext4_crypt.cpp b/crypto/ext4crypt/ext4_crypt.cpp
new file mode 100644
index 0000000..5a3b4b2
--- /dev/null
+++ b/crypto/ext4crypt/ext4_crypt.cpp
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* TWRP NOTE: Kanged from system/extras/ext4_utils/ext4_crypt.cpp
+ * because policy_to_hex, e4crypt_policy_set, and e4crypt_policy_get
+ * are not exposed to be used. There was also a bug in e4crypt_policy_get
+ * that may or may not be fixed in the user's local repo:
+ * https://android.googlesource.com/platform/system/extras/+/30b93dd5715abcabd621235733733c0503f9c552
+ */
+
+#include "ext4_crypt.h"
+#include "ext4crypt_tar.h"
+
+#include <dirent.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <fcntl.h>
+#include <asm/ioctl.h>
+#include <sys/syscall.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <cutils/properties.h>
+
+#define XATTR_NAME_ENCRYPTION_POLICY "encryption.policy"
+#define EXT4_KEYREF_DELIMITER ((char)'.')
+
+#define EXT4_ENCRYPTION_MODE_AES_256_XTS 1
+#define EXT4_ENCRYPTION_MODE_AES_256_CTS 4
+#define EXT4_ENCRYPTION_MODE_AES_256_HEH 126
+#define EXT4_ENCRYPTION_MODE_PRIVATE 127
+
+static int encryption_mode = EXT4_ENCRYPTION_MODE_PRIVATE;
+
+#define HEX_LOOKUP "0123456789abcdef"
+
+extern "C" void policy_to_hex(const char* policy, char* hex) {
+ for (size_t i = 0, j = 0; i < EXT4_KEY_DESCRIPTOR_SIZE; i++) {
+ hex[j++] = HEX_LOOKUP[(policy[i] & 0xF0) >> 4];
+ hex[j++] = HEX_LOOKUP[policy[i] & 0x0F];
+ }
+ hex[EXT4_KEY_DESCRIPTOR_SIZE_HEX - 1] = '\0';
+}
+
+extern "C" bool e4crypt_policy_set(const char *directory, const char *policy,
+ size_t policy_length, int contents_encryption_mode) {
+ if (contents_encryption_mode == 0)
+ contents_encryption_mode = encryption_mode;
+ if (policy_length != EXT4_KEY_DESCRIPTOR_SIZE) {
+ printf("policy wrong length\n");
+ LOG(ERROR) << "Policy wrong length: " << policy_length;
+ return false;
+ }
+ int fd = open(directory, O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC);
+ if (fd == -1) {
+ printf("failed to open %s\n", directory);
+ PLOG(ERROR) << "Failed to open directory " << directory;
+ return false;
+ }
+
+ ext4_encryption_policy eep;
+ eep.version = 0;
+ eep.contents_encryption_mode = contents_encryption_mode;
+ eep.filenames_encryption_mode = EXT4_ENCRYPTION_MODE_AES_256_CTS;
+ eep.flags = 0;
+ memcpy(eep.master_key_descriptor, policy, EXT4_KEY_DESCRIPTOR_SIZE);
+ if (ioctl(fd, EXT4_IOC_SET_ENCRYPTION_POLICY, &eep)) {
+ printf("failed to set policy for '%s' '%s'\n", directory, policy);
+ PLOG(ERROR) << "Failed to set encryption policy for " << directory;
+ close(fd);
+ return false;
+ }
+ close(fd);
+
+ char policy_hex[EXT4_KEY_DESCRIPTOR_SIZE_HEX];
+ policy_to_hex(policy, policy_hex);
+ LOG(INFO) << "Policy for " << directory << " set to " << policy_hex;
+ return true;
+}
+
+extern "C" bool e4crypt_policy_get(const char *directory, char *policy,
+ size_t policy_length, int contents_encryption_mode) {
+ if (contents_encryption_mode == 0)
+ contents_encryption_mode = encryption_mode;
+ if (policy_length != EXT4_KEY_DESCRIPTOR_SIZE) {
+ LOG(ERROR) << "Policy wrong length: " << policy_length;
+ return false;
+ }
+
+ int fd = open(directory, O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC);
+ if (fd == -1) {
+ PLOG(ERROR) << "Failed to open directory " << directory;
+ return false;
+ }
+
+ ext4_encryption_policy eep;
+ memset(&eep, 0, sizeof(ext4_encryption_policy));
+ if (ioctl(fd, EXT4_IOC_GET_ENCRYPTION_POLICY, &eep) != 0) {
+ PLOG(ERROR) << "Failed to get encryption policy for " << directory;
+ close(fd);
+ return false;
+ }
+ close(fd);
+
+ if ((eep.version != 0)
+ || (eep.contents_encryption_mode != contents_encryption_mode)
+ || (eep.filenames_encryption_mode != EXT4_ENCRYPTION_MODE_AES_256_CTS)
+ || (eep.flags != 0)) {
+ LOG(ERROR) << "Failed to find matching encryption policy for " << directory;
+ return false;
+ }
+ memcpy(policy, eep.master_key_descriptor, EXT4_KEY_DESCRIPTOR_SIZE);
+
+ return true;
+}
+
+extern "C" void e4crypt_policy_fill_default_struct(ext4_encryption_policy *eep) {
+ eep->version = 0;
+ eep->contents_encryption_mode = encryption_mode;
+ eep->filenames_encryption_mode = EXT4_ENCRYPTION_MODE_AES_256_CTS;
+ eep->flags = 0;
+ memset((void*)&eep->master_key_descriptor[0], 0, EXT4_KEY_DESCRIPTOR_SIZE);
+}
+
+extern "C" bool e4crypt_policy_set_struct(const char *directory, const ext4_encryption_policy *eep) {
+ int fd = open(directory, O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC);
+ if (fd == -1) {
+ printf("failed to open %s\n", directory);
+ PLOG(ERROR) << "Failed to open directory " << directory;
+ return false;
+ }
+ if (ioctl(fd, EXT4_IOC_SET_ENCRYPTION_POLICY, eep)) {
+ printf("failed to set policy for '%s'\n", directory);
+ PLOG(ERROR) << "Failed to set encryption policy for " << directory;
+ close(fd);
+ return false;
+ }
+ close(fd);
+ return true;
+}
+
+extern "C" bool e4crypt_policy_get_struct(const char *directory, ext4_encryption_policy *eep) {
+ int fd = open(directory, O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC);
+ if (fd == -1) {
+ printf("Failed to open '%s'\n", directory);
+ PLOG(ERROR) << "Failed to open directory " << directory;
+ return false;
+ }
+ memset(eep, 0, sizeof(ext4_encryption_policy));
+ if (ioctl(fd, EXT4_IOC_GET_ENCRYPTION_POLICY, eep) != 0) {
+ PLOG(ERROR) << "Failed to get encryption policy for " << directory;
+ close(fd);
+ return false;
+ }
+ close(fd);
+ return true;
+}
+
+extern "C" bool e4crypt_set_mode() {
+ const char* mode_file = "/data/unencrypted/mode";
+ struct stat st;
+ if (stat(mode_file, &st) != 0 || st.st_size <= 0) {
+ printf("Invalid encryption mode file %s\n", mode_file);
+ return false;
+ }
+ size_t mode_size = st.st_size;
+ char contents_encryption_mode[mode_size + 1];
+ memset((void*)contents_encryption_mode, 0, mode_size + 1);
+ int fd = open(mode_file, O_RDONLY);
+ if (fd < 0) {
+ printf("error opening '%s': %s\n", mode_file, strerror(errno));
+ return false;
+ }
+ if (read(fd, contents_encryption_mode, mode_size) != mode_size) {
+ printf("read error on '%s': %s\n", mode_file, strerror(errno));
+ close(fd);
+ return false;
+ }
+ close(fd);
+ if (!strcmp(contents_encryption_mode, "software")) {
+ encryption_mode = EXT4_ENCRYPTION_MODE_AES_256_XTS;
+ } else if (!strcmp(contents_encryption_mode, "ice")) {
+ encryption_mode = EXT4_ENCRYPTION_MODE_PRIVATE;
+ } else {
+ printf("Invalid encryption mode '%s'\n", contents_encryption_mode);
+ return false;
+ }
+ printf("set encryption mode to %i\n", encryption_mode);
+ return true;
+}
diff --git a/crypto/ext4crypt/ext4_crypt.h b/crypto/ext4crypt/ext4_crypt.h
new file mode 100644
index 0000000..d410ccf
--- /dev/null
+++ b/crypto/ext4crypt/ext4_crypt.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _EXT4_CRYPT_H_
+#define _EXT4_CRYPT_H_
+
+#include <sys/cdefs.h>
+#include <stdbool.h>
+#include <cutils/multiuser.h>
+
+__BEGIN_DECLS
+
+bool e4crypt_is_native();
+
+int e4crypt_policy_ensure(const char *directory, const char *policy,
+ size_t policy_length,
+ const char *contents_encryption_mode,
+ const char *filenames_encryption_mode);
+
+static const char* e4crypt_unencrypted_folder = "/unencrypted";
+static const char* e4crypt_key_ref = "/unencrypted/ref";
+static const char* e4crypt_key_mode = "/unencrypted/mode";
+
+__END_DECLS
+
+#endif // _EXT4_CRYPT_H_
diff --git a/crypto/ext4crypt/ext4crypt_tar.h b/crypto/ext4crypt/ext4crypt_tar.h
new file mode 100644
index 0000000..c35d115
--- /dev/null
+++ b/crypto/ext4crypt/ext4crypt_tar.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2016 Team Win Recovery Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __EXT4CRYPT_TAR_H
+#define __EXT4CRYPT_TAR_H
+
+#include <sys/cdefs.h>
+#include <stdbool.h>
+#include <cutils/multiuser.h>
+
+// ext4enc:TODO Include structure from somewhere sensible
+// MUST be in sync with ext4_crypto.c in kernel
+#define EXT4_KEY_DESCRIPTOR_SIZE 8
+#define EXT4_KEY_DESCRIPTOR_SIZE_HEX 17
+
+// ext4enc:TODO Get value from somewhere sensible
+#define EXT4_IOC_SET_ENCRYPTION_POLICY _IOR('f', 19, struct ext4_encryption_policy)
+#define EXT4_IOC_GET_ENCRYPTION_POLICY _IOW('f', 21, struct ext4_encryption_policy)
+
+__BEGIN_DECLS
+
+struct ext4_encryption_policy {
+ char version;
+ char contents_encryption_mode;
+ char filenames_encryption_mode;
+ char flags;
+ char master_key_descriptor[EXT4_KEY_DESCRIPTOR_SIZE];
+} __attribute__((__packed__));
+
+bool lookup_ref_key(const char* policy, char* policy_type);
+bool lookup_ref_tar(const char* policy_type, char* policy);
+
+void policy_to_hex(const char* policy, char* hex);
+bool e4crypt_policy_set(const char *directory, const char *policy,
+ size_t policy_length, int contents_encryption_mode);
+bool e4crypt_policy_get(const char *directory, char *policy,
+ size_t policy_length, int contents_encryption_mode);
+void e4crypt_policy_fill_default_struct(struct ext4_encryption_policy *eep);
+bool e4crypt_policy_set_struct(const char *directory, const struct ext4_encryption_policy *eep);
+bool e4crypt_policy_get_struct(const char *directory, struct ext4_encryption_policy *eep);
+
+bool e4crypt_set_mode();
+__END_DECLS
+
+#endif
diff --git a/crypto/ext4crypt/keystore_auth.cpp b/crypto/ext4crypt/keystore_auth.cpp
new file mode 100644
index 0000000..40d890f
--- /dev/null
+++ b/crypto/ext4crypt/keystore_auth.cpp
@@ -0,0 +1,107 @@
+/*
+ Copyright 2018 bigbiff/Dees_Troy TeamWin
+ This file is part of TWRP/TeamWin Recovery Project.
+
+ TWRP is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ TWRP is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with TWRP. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* The keystore refuses to allow the root user to supply auth tokens, so
+ * we write the auth token to a file in TWRP and run a separate service
+ * (this) that runs as the system user to add the auth token. TWRP waits
+ * for /auth_token to be deleted and also looks for /auth_error to check
+ * for errors. TWRP will error out after a while if /auth_token does not
+ * get deleted. */
+
+#include <stdio.h>
+#include <string>
+
+#ifdef USE_SECURITY_NAMESPACE
+#include <android/security/IKeystoreService.h>
+#else
+#include <keystore/IKeystoreService.h>
+#include <keystore/authorization_set.h>
+#endif
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+
+#include <keystore/keystore.h>
+
+#ifndef LOG_TAG
+#define LOG_TAG "keystore_auth"
+#endif
+
+using namespace android;
+
+void create_error_file() {
+ FILE* error_file = fopen("/auth_error", "wb");
+ if (error_file == NULL) {
+ printf("Failed to open /auth_error\n");
+ ALOGE("Failed to open /auth_error\n");
+ return;
+ }
+ fwrite("1", 1, 1, error_file);
+ fclose(error_file);
+ unlink("/auth_token");
+}
+
+int main() {
+ unlink("/auth_error");
+ FILE* auth_file = fopen("/auth_token", "rb");
+ if (auth_file == NULL) {
+ printf("Failed to open /auth_token\n");
+ ALOGE("Failed to open /auth_token\n");
+ create_error_file();
+ return -1;
+ }
+ // Get the file size
+ fseek(auth_file, 0, SEEK_END);
+ int size = ftell(auth_file);
+ fseek(auth_file, 0, SEEK_SET);
+ uint8_t auth_token[size];
+ fread(auth_token , sizeof(uint8_t), size, auth_file);
+ fclose(auth_file);
+ // First get the keystore service
+ sp<IServiceManager> sm = defaultServiceManager();
+ sp<IBinder> binder = sm->getService(String16("android.security.keystore"));
+#ifdef USE_SECURITY_NAMESPACE
+ sp<security::IKeystoreService> service = interface_cast<security::IKeystoreService>(binder);
+#else
+ sp<IKeystoreService> service = interface_cast<IKeystoreService>(binder);
+#endif
+ if (service == NULL) {
+ printf("error: could not connect to keystore service\n");
+ ALOGE("error: could not connect to keystore service\n");
+ create_error_file();
+ return -2;
+ }
+#ifdef USE_SECURITY_NAMESPACE
+ std::vector<uint8_t> auth_token_vector(&auth_token[0], (&auth_token[0]) + size);
+ int result = 0;
+ auto binder_result = service->addAuthToken(auth_token_vector, &result);
+ if (!binder_result.isOk() || !keystore::KeyStoreServiceReturnCode(result).isOk()) {
+#else
+ ::keystore::KeyStoreServiceReturnCode auth_result = service->addAuthToken(auth_token, size);
+ if (!auth_result.isOk()) {
+#endif
+ // The keystore checks the uid of the calling process and will return a permission denied on this operation for user 0
+ printf("keystore error adding auth token\n");
+ ALOGE("keystore error adding auth token\n");
+ create_error_file();
+ return -3;
+ }
+ printf("successfully added auth token to keystore\n");
+ ALOGD("successfully added auth token to keystore\n");
+ unlink("/auth_token");
+ return 0;
+}
diff --git a/crypto/ext4crypt/main.cpp b/crypto/ext4crypt/main.cpp
new file mode 100644
index 0000000..f0266ae
--- /dev/null
+++ b/crypto/ext4crypt/main.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2016 Team Win Recovery Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "Decrypt.h"
+
+int main(int argc, char *argv[]) {
+ bool ret = false;
+ if (argc < 2) {
+ Decrypt_DE();
+ ret = Decrypt_User(0, "0000");
+ } else if (argc < 3) {
+ Decrypt_DE();
+ ret = Decrypt_User(0, argv[1]);
+ } else {
+ ret = Decrypt_User(atoi(argv[1]), argv[2]);
+ }
+ if (!ret)
+ printf("Failed to decrypt\n");
+ return 0;
+}
diff --git a/crypto/fde/Android.mk b/crypto/fde/Android.mk
new file mode 100755
index 0000000..f78697f
--- /dev/null
+++ b/crypto/fde/Android.mk
@@ -0,0 +1,124 @@
+LOCAL_PATH := $(call my-dir)
+ifeq ($(TW_INCLUDE_CRYPTO), true)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libcryptfsfde
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := cryptfs.cpp
+LOCAL_SHARED_LIBRARIES := libcrypto libhardware libcutils libstdc++
+LOCAL_STATIC_LIBRARIES := libscrypttwrp_static
+LOCAL_C_INCLUDES := external/openssl/include $(commands_recovery_local_path)/crypto/scrypt/lib/crypto
+ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 23; echo $$?),0)
+ LOCAL_C_INCLUDES += bionic external/stlport/stlport
+ LOCAL_SHARED_LIBRARIES += libstlport
+ LOCAL_CPPFLAGS := -std=c++11
+endif
+
+ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 26; echo $$?),0)
+ #8.0 or higher
+ LOCAL_C_INCLUDES += external/boringssl/src/include
+ ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 29; echo $$?),0)
+ LOCAL_SHARED_LIBRARIES += libtwrpfscrypt
+ else
+ LOCAL_SHARED_LIBRARIES += libe4crypt
+ endif
+ LOCAL_SHARED_LIBRARIES += libselinux libc libc++ libbase libcrypto libcutils libkeymaster_messages libhardware libprotobuf-cpp-lite \
+ android.hardware.keymaster@3.0 libkeystore_binder libhidlbase libutils libbinder
+ ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 28; echo $$?),0)
+ #9.0 rules
+ LOCAL_CFLAGS += -Wno-unused-variable -Wno-sign-compare -Wno-unused-parameter -Wno-comment
+ LOCAL_SHARED_LIBRARIES += android.hardware.keymaster@4.0 libkeymaster4support libkeyutils
+ LOCAL_CFLAGS += -DTW_KEYMASTER_MAX_API=4
+ else
+ #8.x rules
+ ifneq ($(wildcard system/core/libkeyutils/Android.bp),)
+ #only present in some 8.0 trees and should be in all 8.1 trees
+ LOCAL_SHARED_LIBRARIES += libkeyutils
+ endif
+ LOCAL_SHARED_LIBRARIES += libsoftkeymaster
+ LOCAL_CFLAGS += -DTW_KEYMASTER_MAX_API=3
+ endif
+else
+ # <= 7.x rules
+ ifneq ($(wildcard hardware/libhardware/include/hardware/keymaster0.h),)
+ LOCAL_C_INCLUDES += external/boringssl/src/include
+ LOCAL_CFLAGS += -DTW_KEYMASTER_MAX_API=1
+ else
+ LOCAL_CFLAGS += -DTW_KEYMASTER_MAX_API=0
+ endif
+endif
+ifeq ($(TARGET_HW_DISK_ENCRYPTION),true)
+ ifeq ($(TARGET_CRYPTFS_HW_PATH),)
+ LOCAL_C_INCLUDES += device/qcom/common/cryptfs_hw
+ else
+ LOCAL_C_INCLUDES += $(TARGET_CRYPTFS_HW_PATH)
+ endif
+ LOCAL_SHARED_LIBRARIES += libcryptfs_hw
+ LOCAL_CFLAGS += -DCONFIG_HW_DISK_ENCRYPTION
+endif
+
+include $(BUILD_SHARED_LIBRARY)
+
+
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := twrpdec
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/
+LOCAL_SRC_FILES := main.cpp cryptfs.cpp
+LOCAL_SHARED_LIBRARIES := libcrypto libhardware libcutils libc libstdc++
+LOCAL_C_INCLUDES := external/openssl/include $(commands_recovery_local_path)/crypto/scrypt/lib/crypto
+ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 23; echo $$?),0)
+ LOCAL_C_INCLUDES += bionic external/stlport/stlport
+ LOCAL_SHARED_LIBRARIES += libstlport
+ LOCAL_CPPFLAGS := -std=c++11
+endif
+
+ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 26; echo $$?),0)
+ #8.0 or higher
+ LOCAL_C_INCLUDES += external/boringssl/src/include
+ ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 29; echo $$?),0)
+ LOCAL_SHARED_LIBRARIES += libtwrpfscrypt
+ else
+ LOCAL_SHARED_LIBRARIES += libe4crypt
+ endif
+ LOCAL_SHARED_LIBRARIES += libselinux libc libc++ libbase libcrypto libcutils libkeymaster_messages libhardware libprotobuf-cpp-lite \
+ android.hardware.keymaster@3.0 libkeystore_binder libhidlbase libutils libbinder
+ ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 28; echo $$?),0)
+ #9.0 rules
+ LOCAL_CFLAGS += -Wno-unused-variable -Wno-sign-compare -Wno-unused-parameter -Wno-comment
+ LOCAL_SHARED_LIBRARIES += android.hardware.keymaster@4.0 libkeymaster4support libkeyutils
+ LOCAL_CFLAGS += -DTW_KEYMASTER_MAX_API=4
+ else
+ #8.x rules
+ ifneq ($(wildcard system/core/libkeyutils/Android.bp),)
+ #only present in some 8.0 trees and should be in all 8.1 trees
+ LOCAL_SHARED_LIBRARIES += libkeyutils
+ endif
+ LOCAL_SHARED_LIBRARIES += libsoftkeymaster
+ LOCAL_CFLAGS += -DTW_KEYMASTER_MAX_API=3
+ endif
+else
+ # <= 7.x rules
+ ifneq ($(wildcard hardware/libhardware/include/hardware/keymaster0.h),)
+ LOCAL_C_INCLUDES += external/boringssl/src/include
+ LOCAL_CFLAGS += -DTW_KEYMASTER_MAX_API=1
+ else
+ LOCAL_CFLAGS += -DTW_KEYMASTER_MAX_API=0
+ endif
+endif
+ifeq ($(TARGET_HW_DISK_ENCRYPTION),true)
+ ifeq ($(TARGET_CRYPTFS_HW_PATH),)
+ LOCAL_C_INCLUDES += device/qcom/common/cryptfs_hw
+ else
+ LOCAL_C_INCLUDES += $(TARGET_CRYPTFS_HW_PATH)
+ endif
+ LOCAL_SHARED_LIBRARIES += libcryptfs_hw
+ LOCAL_CFLAGS += -DCONFIG_HW_DISK_ENCRYPTION
+endif
+
+LOCAL_WHOLE_STATIC_LIBRARIES += libscrypttwrp_static
+include $(BUILD_EXECUTABLE)
+
+endif
diff --git a/crypto/fde/cryptfs.cpp b/crypto/fde/cryptfs.cpp
new file mode 100644
index 0000000..7c30eef
--- /dev/null
+++ b/crypto/fde/cryptfs.cpp
@@ -0,0 +1,1767 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* TO DO:
+ * 1. Perhaps keep several copies of the encrypted key, in case something
+ * goes horribly wrong?
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <linux/dm-ioctl.h>
+#include <libgen.h>
+#include <stdlib.h>
+#include <sys/param.h>
+#include <string.h>
+#include <sys/mount.h>
+#include <openssl/evp.h>
+#include <openssl/sha.h>
+#include <errno.h>
+//#include <ext4_utils/ext4_crypt.h>
+//#include <ext4_utils/ext4_utils.h>
+#include <linux/kdev_t.h>
+//#include <fs_mgr.h>
+#include <time.h>
+#include <math.h>
+//#include <selinux/selinux.h>
+#include "cryptfs.h"
+//#include "secontext.h"
+#define LOG_TAG "Cryptfs"
+//#include "cutils/log.h"
+#include "cutils/properties.h"
+//#include "cutils/android_reboot.h"
+//#include "hardware_legacy/power.h"
+//#include <logwrap/logwrap.h>
+//#include "ScryptParameters.h"
+//#include "VolumeManager.h"
+//#include "VoldUtil.h"
+//#include "Ext4Crypt.h"
+//#include "f2fs_sparseblock.h"
+//#include "EncryptInplace.h"
+//#include "Process.h"
+#if TW_KEYMASTER_MAX_API == 3
+#include "../ext4crypt/Keymaster3.h"
+#endif
+#if TW_KEYMASTER_MAX_API == 4
+#include "../ext4crypt/Keymaster4.h"
+#endif
+#if TW_KEYMASTER_MAX_API == 0
+#include <hardware/keymaster.h>
+#else // so far, all trees that have keymaster >= 1 have keymaster 1 support
+#include <stdbool.h>
+#include <openssl/evp.h>
+#include <openssl/sha.h>
+#include <hardware/keymaster0.h>
+#include <hardware/keymaster1.h>
+#endif
+//#include "android-base/properties.h"
+//#include <bootloader_message/bootloader_message.h>
+#ifdef CONFIG_HW_DISK_ENCRYPTION
+#include <cryptfs_hw.h>
+#endif
+extern "C" {
+#include <crypto_scrypt.h>
+}
+#include <string>
+#include <vector>
+
+#define ALOGE(...) fprintf(stdout, "E:" __VA_ARGS__)
+#define SLOGE(...) fprintf(stdout, "E:" __VA_ARGS__)
+#define SLOGW(...) fprintf(stdout, "W:" __VA_ARGS__)
+#define SLOGI(...) fprintf(stdout, "I:" __VA_ARGS__)
+#define SLOGD(...) fprintf(stdout, "D:" __VA_ARGS__)
+
+#define UNUSED __attribute__((unused))
+
+#define DM_CRYPT_BUF_SIZE 4096
+
+#define HASH_COUNT 2000
+
+#ifndef min /* already defined by windows.h */
+#define min(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+constexpr size_t INTERMEDIATE_KEY_LEN_BYTES = 16;
+constexpr size_t INTERMEDIATE_IV_LEN_BYTES = 16;
+constexpr size_t INTERMEDIATE_BUF_SIZE =
+ (INTERMEDIATE_KEY_LEN_BYTES + INTERMEDIATE_IV_LEN_BYTES);
+
+// SCRYPT_LEN is used by struct crypt_mnt_ftr for its intermediate key.
+static_assert(INTERMEDIATE_BUF_SIZE == SCRYPT_LEN,
+ "Mismatch of intermediate key sizes");
+
+#define KEY_IN_FOOTER "footer"
+
+#define DEFAULT_HEX_PASSWORD "64656661756c745f70617373776f7264"
+#define DEFAULT_PASSWORD "default_password"
+
+#define CRYPTO_BLOCK_DEVICE "userdata"
+
+#define TABLE_LOAD_RETRIES 10
+
+#define RSA_KEY_SIZE 2048
+#define RSA_KEY_SIZE_BYTES (RSA_KEY_SIZE / 8)
+#define RSA_EXPONENT 0x10001
+#define KEYMASTER_CRYPTFS_RATE_LIMIT 1 // Maximum one try per second
+#define KEY_LEN_BYTES 16
+
+#define RETRY_MOUNT_ATTEMPTS 10
+#define RETRY_MOUNT_DELAY_SECONDS 1
+
+#define CREATE_CRYPTO_BLK_DEV_FLAGS_ALLOW_ENCRYPT_OVERRIDE (1)
+
+static unsigned char saved_master_key[MAX_KEY_LEN];
+static char *saved_mount_point;
+static int master_key_saved = 0;
+static struct crypt_persist_data *persist_data = NULL;
+
+static int previous_type;
+
+static char key_fname[PROPERTY_VALUE_MAX] = "";
+static char real_blkdev[PROPERTY_VALUE_MAX] = "";
+static char file_system[PROPERTY_VALUE_MAX] = "";
+
+static void get_blkdev_size(int fd, unsigned long *nr_sec)
+{
+ if ( (ioctl(fd, BLKGETSIZE, nr_sec)) == -1) {
+ *nr_sec = 0;
+ }
+}
+
+#if TW_KEYMASTER_MAX_API == 0
+static int keymaster_init(keymaster_device_t **keymaster_dev)
+{
+ int rc;
+
+ const hw_module_t* mod;
+ rc = hw_get_module_by_class(KEYSTORE_HARDWARE_MODULE_ID, NULL, &mod);
+ if (rc) {
+ printf("could not find any keystore module\n");
+ goto out;
+ }
+
+ rc = keymaster_open(mod, keymaster_dev);
+ if (rc) {
+ printf("could not open keymaster device in %s (%s)\n",
+ KEYSTORE_HARDWARE_MODULE_ID, strerror(-rc));
+ goto out;
+ }
+
+ return 0;
+
+out:
+ *keymaster_dev = NULL;
+ return rc;
+}
+#else //TW_KEYMASTER_MAX_API == 0
+static int keymaster_init(keymaster0_device_t **keymaster0_dev,
+ keymaster1_device_t **keymaster1_dev)
+{
+ int rc;
+
+ const hw_module_t* mod;
+ rc = hw_get_module_by_class(KEYSTORE_HARDWARE_MODULE_ID, NULL, &mod);
+ if (rc) {
+ printf("could not find any keystore module\n");
+ goto err;
+ }
+
+ printf("keymaster module name is %s\n", mod->name);
+ printf("keymaster version is %d\n", mod->module_api_version);
+
+ *keymaster0_dev = NULL;
+ *keymaster1_dev = NULL;
+ if (mod->module_api_version == KEYMASTER_MODULE_API_VERSION_1_0) {
+ printf("Found keymaster1 module, using keymaster1 API.\n");
+ rc = keymaster1_open(mod, keymaster1_dev);
+ } else {
+ printf("Found keymaster0 module, using keymaster0 API.\n");
+ rc = keymaster0_open(mod, keymaster0_dev);
+ }
+
+ if (rc) {
+ printf("could not open keymaster device in %s (%s)\n",
+ KEYSTORE_HARDWARE_MODULE_ID, strerror(-rc));
+ goto err;
+ }
+
+ return 0;
+
+err:
+ *keymaster0_dev = NULL;
+ *keymaster1_dev = NULL;
+ return rc;
+}
+#endif //TW_KEYMASTER_MAX_API == 0
+
+#ifdef CONFIG_HW_DISK_ENCRYPTION
+static int scrypt_keymaster(const char *passwd, const unsigned char *salt,
+ unsigned char *ikey, void *params);
+static void convert_key_to_hex_ascii(const unsigned char *master_key,
+ unsigned int keysize, char *master_key_ascii);
+static int test_mount_hw_encrypted_fs(struct crypt_mnt_ftr* crypt_ftr,
+ const char *passwd, const char *mount_point, const char *label);
+int cryptfs_check_passwd_hw(char *passwd);
+int cryptfs_get_master_key(struct crypt_mnt_ftr* ftr, const char* password,
+ unsigned char* master_key);
+
+static void convert_key_to_hex_ascii_for_upgrade(const unsigned char *master_key,
+ unsigned int keysize, char *master_key_ascii)
+{
+ unsigned int i, a;
+ unsigned char nibble;
+
+ for (i = 0, a = 0; i < keysize; i++, a += 2) {
+ /* For each byte, write out two ascii hex digits */
+ nibble = (master_key[i] >> 4) & 0xf;
+ master_key_ascii[a] = nibble + (nibble > 9 ? 0x57 : 0x30);
+
+ nibble = master_key[i] & 0xf;
+ master_key_ascii[a + 1] = nibble + (nibble > 9 ? 0x57 : 0x30);
+ }
+
+ /* Add the null termination */
+ master_key_ascii[a] = '\0';
+}
+
+static int get_keymaster_hw_fde_passwd(const char* passwd, unsigned char* newpw,
+ unsigned char* salt,
+ const struct crypt_mnt_ftr *ftr)
+{
+ /* if newpw updated, return 0
+ * if newpw not updated return -1
+ */
+ int rc = -1;
+
+ if (should_use_keymaster()) {
+ if (scrypt_keymaster(passwd, salt, newpw, (void*)ftr)) {
+ SLOGE("scrypt failed");
+ } else {
+ rc = 0;
+ }
+ }
+
+ return rc;
+}
+
+static int verify_hw_fde_passwd(const char *passwd, struct crypt_mnt_ftr* crypt_ftr)
+{
+ unsigned char newpw[32] = {0};
+ int key_index;
+ SLOGI("starting verify_hw_fde_passwd\n");
+ if (get_keymaster_hw_fde_passwd(passwd, newpw, crypt_ftr->salt, crypt_ftr))
+ key_index = set_hw_device_encryption_key(passwd,
+ (char*) crypt_ftr->crypto_type_name);
+ else
+ key_index = set_hw_device_encryption_key((const char*)newpw,
+ (char*) crypt_ftr->crypto_type_name);
+ return key_index;
+}
+
+static int verify_and_update_hw_fde_passwd(const char *passwd,
+ struct crypt_mnt_ftr* crypt_ftr)
+{
+ char* new_passwd = NULL;
+ unsigned char newpw[32] = {0};
+ int key_index = -1;
+ int passwd_updated = -1;
+ int ascii_passwd_updated = (crypt_ftr->flags & CRYPT_ASCII_PASSWORD_UPDATED);
+
+ key_index = verify_hw_fde_passwd(passwd, crypt_ftr);
+ if (key_index < 0) {
+ ++crypt_ftr->failed_decrypt_count;
+
+ if (ascii_passwd_updated) {
+ SLOGI("Ascii password was updated");
+ } else {
+ /* Code in else part would execute only once:
+ * When device is upgraded from L->M release.
+ * Once upgraded, code flow should never come here.
+ * L release passed actual password in hex, so try with hex
+ * Each nible of passwd was encoded as a byte, so allocate memory
+ * twice of password len plus one more byte for null termination
+ */
+ if (crypt_ftr->crypt_type == CRYPT_TYPE_DEFAULT) {
+ new_passwd = (char*)malloc(strlen(DEFAULT_HEX_PASSWORD) + 1);
+ if (new_passwd == NULL) {
+ SLOGE("System out of memory. Password verification incomplete");
+ goto out;
+ }
+ strlcpy(new_passwd, DEFAULT_HEX_PASSWORD, strlen(DEFAULT_HEX_PASSWORD) + 1);
+ } else {
+ new_passwd = (char*)malloc(strlen(passwd) * 2 + 1);
+ if (new_passwd == NULL) {
+ SLOGE("System out of memory. Password verification incomplete");
+ goto out;
+ }
+ convert_key_to_hex_ascii_for_upgrade((const unsigned char*)passwd,
+ strlen(passwd), new_passwd);
+ }
+ key_index = set_hw_device_encryption_key((const char*)new_passwd,
+ (char*) crypt_ftr->crypto_type_name);
+ if (key_index >=0) {
+ crypt_ftr->failed_decrypt_count = 0;
+ SLOGI("Hex password verified...will try to update with Ascii value");
+ /* Before updating password, tie that with keymaster to tie with ROT */
+
+ if (get_keymaster_hw_fde_passwd(passwd, newpw,
+ crypt_ftr->salt, crypt_ftr)) {
+ passwd_updated = update_hw_device_encryption_key(new_passwd,
+ passwd, (char*)crypt_ftr->crypto_type_name);
+ } else {
+ passwd_updated = update_hw_device_encryption_key(new_passwd,
+ (const char*)newpw, (char*)crypt_ftr->crypto_type_name);
+ }
+
+ if (passwd_updated >= 0) {
+ crypt_ftr->flags |= CRYPT_ASCII_PASSWORD_UPDATED;
+ SLOGI("Ascii password recorded and updated");
+ } else {
+ SLOGI("Passwd verified, could not update...Will try next time");
+ }
+ } else {
+ ++crypt_ftr->failed_decrypt_count;
+ }
+ free(new_passwd);
+ }
+ } else {
+ if (!ascii_passwd_updated)
+ crypt_ftr->flags |= CRYPT_ASCII_PASSWORD_UPDATED;
+ }
+out:
+ // update footer before leaving
+ //put_crypt_ftr_and_key(crypt_ftr);
+ return key_index;
+}
+#endif
+
+void set_partition_data(const char* block_device, const char* key_location, const char* fs)
+{
+ strcpy(key_fname, key_location);
+ strcpy(real_blkdev, block_device);
+ strcpy(file_system, fs);
+}
+
+/* This signs the given object using the keymaster key. */
+static int keymaster_sign_object(struct crypt_mnt_ftr *ftr,
+ const unsigned char *object,
+ const size_t object_size,
+ unsigned char **signature,
+ size_t *signature_size)
+{
+ SLOGI("TWRP keymaster max API: %i\n", TW_KEYMASTER_MAX_API);
+ unsigned char to_sign[RSA_KEY_SIZE_BYTES];
+ size_t to_sign_size = sizeof(to_sign);
+ memset(to_sign, 0, RSA_KEY_SIZE_BYTES);
+
+ // To sign a message with RSA, the message must satisfy two
+ // constraints:
+ //
+ // 1. The message, when interpreted as a big-endian numeric value, must
+ // be strictly less than the public modulus of the RSA key. Note
+ // that because the most significant bit of the public modulus is
+ // guaranteed to be 1 (else it's an (n-1)-bit key, not an n-bit
+ // key), an n-bit message with most significant bit 0 always
+ // satisfies this requirement.
+ //
+ // 2. The message must have the same length in bits as the public
+ // modulus of the RSA key. This requirement isn't mathematically
+ // necessary, but is necessary to ensure consistency in
+ // implementations.
+ switch (ftr->kdf_type) {
+ case KDF_SCRYPT_KEYMASTER_UNPADDED:
+ // This is broken: It produces a message which is shorter than
+ // the public modulus, failing criterion 2.
+ memcpy(to_sign, object, object_size);
+ to_sign_size = object_size;
+ SLOGI("Signing unpadded object\n");
+ break;
+ case KDF_SCRYPT_KEYMASTER_BADLY_PADDED:
+ // This is broken: Since the value of object is uniformly
+ // distributed, it produces a message that is larger than the
+ // public modulus with probability 0.25.
+ memcpy(to_sign, object, min(RSA_KEY_SIZE_BYTES, object_size));
+ SLOGI("Signing end-padded object\n");
+ break;
+ case KDF_SCRYPT_KEYMASTER:
+ // This ensures the most significant byte of the signed message
+ // is zero. We could have zero-padded to the left instead, but
+ // this approach is slightly more robust against changes in
+ // object size. However, it's still broken (but not unusably
+ // so) because we really should be using a proper deterministic
+ // RSA padding function, such as PKCS1.
+ memcpy(to_sign + 1, object, min((size_t)RSA_KEY_SIZE_BYTES - 1, object_size));
+ SLOGI("Signing safely-padded object");
+ break;
+ default:
+ SLOGE("Unknown KDF type %d", ftr->kdf_type);
+ return -1;
+ }
+
+ int rc = -1;
+
+#if TW_KEYMASTER_MAX_API >= 1
+ keymaster0_device_t *keymaster0_dev = 0;
+ keymaster1_device_t *keymaster1_dev = 0;
+ if (keymaster_init(&keymaster0_dev, &keymaster1_dev)) {
+#else
+ keymaster_device_t *keymaster0_dev = 0;
+ if (keymaster_init(&keymaster0_dev)) {
+#endif
+ printf("Failed to init keymaster 0/1\n");
+ goto initfail;
+ }
+ if (keymaster0_dev) {
+ keymaster_rsa_sign_params_t params;
+ params.digest_type = DIGEST_NONE;
+ params.padding_type = PADDING_NONE;
+
+ rc = keymaster0_dev->sign_data(keymaster0_dev,
+ ¶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;
+ }
+#endif // TW_KEYMASTER_API >= 1
+
+ out:
+#if TW_KEYMASTER_MAX_API >= 1
+ if (keymaster1_dev)
+ keymaster1_close(keymaster1_dev);
+#endif
+ if (keymaster0_dev)
+#if TW_KEYMASTER_MAX_API >= 1
+ keymaster0_close(keymaster0_dev);
+#else
+ keymaster_close(keymaster0_dev);
+#endif
+
+ if (rc == 0)
+ return 0; // otherwise we'll try for a newer keymaster API
+
+initfail:
+#if TW_KEYMASTER_MAX_API == 3
+ return keymaster_sign_object_for_cryptfs_scrypt(ftr->keymaster_blob, ftr->keymaster_blob_size,
+ KEYMASTER_CRYPTFS_RATE_LIMIT, to_sign, to_sign_size, signature, signature_size,
+ ftr->keymaster_blob, KEYMASTER_BLOB_SIZE, &ftr->keymaster_blob_size);
+#endif //TW_KEYMASTER_MAX_API == 3
+#if TW_KEYMASTER_MAX_API >= 4
+ for (int c = 1;c <= 20;c++) { // 20 tries are enough for signing keymaster
+ if (c > 2)
+ usleep(5000); // if failed in two tries lets rest
+ auto result = keymaster_sign_object_for_cryptfs_scrypt(
+ ftr->keymaster_blob, ftr->keymaster_blob_size, KEYMASTER_CRYPTFS_RATE_LIMIT, to_sign,
+ to_sign_size, signature, signature_size);
+ switch (result) {
+ case KeymasterSignResult::ok:
+ return 0;
+ case KeymasterSignResult::upgrade:
+ break;
+ default:
+ return -1;
+ }
+ SLOGD("Upgrading key\n");
+ if (keymaster_upgrade_key_for_cryptfs_scrypt(
+ RSA_KEY_SIZE, RSA_EXPONENT, KEYMASTER_CRYPTFS_RATE_LIMIT, ftr->keymaster_blob,
+ ftr->keymaster_blob_size, ftr->keymaster_blob, KEYMASTER_BLOB_SIZE,
+ &ftr->keymaster_blob_size) != 0) {
+ SLOGE("Failed to upgrade key\n");
+ return -1;
+ }
+ /*if (put_crypt_ftr_and_key(ftr) != 0) {
+ SLOGE("Failed to write upgraded key to disk");
+ }*/
+ SLOGD("Key upgraded successfully\n");
+ }
+#endif
+ return -1;
+}
+
+static void ioctl_init(struct dm_ioctl *io, size_t dataSize, const char *name, unsigned flags)
+{
+ memset(io, 0, dataSize);
+ io->data_size = dataSize;
+ io->data_start = sizeof(struct dm_ioctl);
+ io->version[0] = 4;
+ io->version[1] = 0;
+ io->version[2] = 0;
+ io->flags = flags;
+ if (name) {
+ strlcpy(io->name, name, sizeof(io->name));
+ }
+}
+
+namespace {
+
+struct CryptoType;
+
+// Use to get the CryptoType in use on this device.
+const CryptoType &get_crypto_type();
+
+struct CryptoType {
+ // We should only be constructing CryptoTypes as part of
+ // supported_crypto_types[]. We do it via this pseudo-builder pattern,
+ // which isn't pure or fully protected as a concession to being able to
+ // do it all at compile time. Add new CryptoTypes in
+ // supported_crypto_types[] below.
+ constexpr CryptoType() : CryptoType(nullptr, nullptr, 0xFFFFFFFF) {}
+ constexpr CryptoType set_keysize(uint32_t size) const {
+ return CryptoType(this->property_name, this->crypto_name, size);
+ }
+ constexpr CryptoType set_property_name(const char *property) const {
+ return CryptoType(property, this->crypto_name, this->keysize);
+ }
+ constexpr CryptoType set_crypto_name(const char *crypto) const {
+ return CryptoType(this->property_name, crypto, this->keysize);
+ }
+
+ constexpr const char *get_property_name() const { return property_name; }
+ constexpr const char *get_crypto_name() const { return crypto_name; }
+ constexpr uint32_t get_keysize() const { return keysize; }
+
+ private:
+ const char *property_name;
+ const char *crypto_name;
+ uint32_t keysize;
+
+ constexpr CryptoType(const char *property, const char *crypto,
+ uint32_t ksize)
+ : property_name(property), crypto_name(crypto), keysize(ksize) {}
+ friend const CryptoType &get_crypto_type();
+ static const CryptoType &get_device_crypto_algorithm();
+};
+
+// We only want to parse this read-only property once. But we need to wait
+// until the system is initialized before we can read it. So we use a static
+// scoped within this function to get it only once.
+const CryptoType &get_crypto_type() {
+ static CryptoType crypto_type = CryptoType::get_device_crypto_algorithm();
+ return crypto_type;
+}
+
+constexpr CryptoType default_crypto_type = CryptoType()
+ .set_property_name("AES-128-CBC")
+ .set_crypto_name("aes-cbc-essiv:sha256")
+ .set_keysize(16);
+
+constexpr CryptoType supported_crypto_types[] = {
+ default_crypto_type,
+ CryptoType()
+ .set_property_name("Speck128/128-XTS")
+ .set_crypto_name("speck128-xts-plain64")
+ .set_keysize(32),
+ // Add new CryptoTypes here. Order is not important.
+};
+
+
+// ---------- START COMPILE-TIME SANITY CHECK BLOCK -------------------------
+// We confirm all supported_crypto_types have a small enough keysize and
+// had both set_property_name() and set_crypto_name() called.
+
+template <typename T, size_t N>
+constexpr size_t array_length(T (&)[N]) { return N; }
+
+constexpr bool indexOutOfBoundsForCryptoTypes(size_t index) {
+ return (index >= array_length(supported_crypto_types));
+}
+
+constexpr bool isValidCryptoType(const CryptoType &crypto_type) {
+ return ((crypto_type.get_property_name() != nullptr) &&
+ (crypto_type.get_crypto_name() != nullptr) &&
+ (crypto_type.get_keysize() <= MAX_KEY_LEN));
+}
+
+// Note in C++11 that constexpr functions can only have a single line.
+// So our code is a bit convoluted (using recursion instead of a loop),
+// but it's asserting at compile time that all of our key lengths are valid.
+constexpr bool validateSupportedCryptoTypes(size_t index) {
+ return indexOutOfBoundsForCryptoTypes(index) ||
+ (isValidCryptoType(supported_crypto_types[index]) &&
+ validateSupportedCryptoTypes(index + 1));
+}
+
+static_assert(validateSupportedCryptoTypes(0),
+ "We have a CryptoType with keysize > MAX_KEY_LEN or which was "
+ "incompletely constructed.");
+// ---------- END COMPILE-TIME SANITY CHECK BLOCK -------------------------
+
+
+// Don't call this directly, use get_crypto_type(), which caches this result.
+const CryptoType &CryptoType::get_device_crypto_algorithm() {
+ constexpr char CRYPT_ALGO_PROP[] = "ro.crypto.fde_algorithm";
+ char paramstr[PROPERTY_VALUE_MAX];
+
+ property_get(CRYPT_ALGO_PROP, paramstr,
+ default_crypto_type.get_property_name());
+ for (auto const &ctype : supported_crypto_types) {
+ if (strcmp(paramstr, ctype.get_property_name()) == 0) {
+ return ctype;
+ }
+ }
+ ALOGE("Invalid name (%s) for %s. Defaulting to %s\n", paramstr,
+ CRYPT_ALGO_PROP, default_crypto_type.get_property_name());
+ return default_crypto_type;
+}
+
+} // namespace
+
+#define SCRYPT_PROP "ro.crypto.scrypt_params"
+#define SCRYPT_DEFAULTS "15:3:1"
+
+bool parse_scrypt_parameters(const char* paramstr, int *Nf, int *rf, int *pf) {
+ int params[3] = {};
+ char *token;
+ char *saveptr;
+ int i;
+
+ /*
+ * The token we're looking for should be three integers separated by
+ * colons (e.g., "12:8:1"). Scan the property to make sure it matches.
+ */
+ for (i = 0, token = strtok_r(const_cast<char *>(paramstr), ":", &saveptr);
+ token != nullptr && i < 3;
+ i++, token = strtok_r(nullptr, ":", &saveptr)) {
+ char *endptr;
+ params[i] = strtol(token, &endptr, 10);
+
+ /*
+ * Check that there was a valid number and it's 8-bit.
+ */
+ if ((*token == '\0') || (*endptr != '\0') || params[i] < 0 || params[i] > 255) {
+ return false;
+ }
+ }
+ if (token != nullptr) {
+ return false;
+ }
+ *Nf = params[0]; *rf = params[1]; *pf = params[2];
+ return true;
+}
+
+uint32_t cryptfs_get_keysize() {
+ return get_crypto_type().get_keysize();
+}
+
+const char *cryptfs_get_crypto_name() {
+ return get_crypto_type().get_crypto_name();
+}
+
+static int get_crypt_ftr_info(char **metadata_fname, off64_t *off)
+{
+ static int cached_data = 0;
+ static off64_t cached_off = 0;
+ static char cached_metadata_fname[PROPERTY_VALUE_MAX] = "";
+ int fd;
+ //char key_loc[PROPERTY_VALUE_MAX];
+ //char real_blkdev[PROPERTY_VALUE_MAX];
+ int rc = -1;
+
+ if (!cached_data) {
+ //fs_mgr_get_crypt_info(fstab_default, key_loc, real_blkdev, sizeof(key_loc));
+
+ if (!strcmp(key_fname, KEY_IN_FOOTER)) {
+ if ( (fd = open(real_blkdev, O_RDWR|O_CLOEXEC)) < 0) {
+ SLOGE("Cannot open real block device %s\n", real_blkdev);
+ return -1;
+ }
+
+ unsigned long nr_sec = 0;
+ get_blkdev_size(fd, &nr_sec);
+ if (nr_sec != 0) {
+ /* If it's an encrypted Android partition, the last 16 Kbytes contain the
+ * encryption info footer and key, and plenty of bytes to spare for future
+ * growth.
+ */
+ strlcpy(cached_metadata_fname, real_blkdev, sizeof(cached_metadata_fname));
+ cached_off = ((off64_t)nr_sec * 512) - CRYPT_FOOTER_OFFSET;
+ cached_data = 1;
+ } else {
+ SLOGE("Cannot get size of block device %s\n", real_blkdev);
+ }
+ close(fd);
+ } else {
+ strlcpy(cached_metadata_fname, key_fname, sizeof(cached_metadata_fname));
+ cached_off = 0;
+ cached_data = 1;
+ }
+ }
+
+ if (cached_data) {
+ if (metadata_fname) {
+ *metadata_fname = cached_metadata_fname;
+ }
+ if (off) {
+ *off = cached_off;
+ }
+ rc = 0;
+ }
+
+ return rc;
+}
+
+static int get_crypt_ftr_and_key(struct crypt_mnt_ftr *crypt_ftr)
+{
+ int fd;
+ unsigned int cnt;
+ off64_t starting_off;
+ int rc = -1;
+ char *fname = NULL;
+ struct stat statbuf;
+
+ if (get_crypt_ftr_info(&fname, &starting_off)) {
+ SLOGE("Unable to get crypt_ftr_info\n");
+ return -1;
+ }
+ if (fname[0] != '/') {
+ SLOGE("Unexpected value for crypto key location\n");
+ return -1;
+ }
+ if ( (fd = open(fname, O_RDWR|O_CLOEXEC)) < 0) {
+ SLOGE("Cannot open footer file %s for get\n", fname);
+ return -1;
+ }
+
+ /* Make sure it's 16 Kbytes in length */
+ fstat(fd, &statbuf);
+ if (S_ISREG(statbuf.st_mode) && (statbuf.st_size != 0x4000)) {
+ SLOGE("footer file %s is not the expected size!\n", fname);
+ goto errout;
+ }
+
+ /* Seek to the start of the crypt footer */
+ if (lseek64(fd, starting_off, SEEK_SET) == -1) {
+ SLOGE("Cannot seek to real block device footer\n");
+ goto errout;
+ }
+
+ if ( (cnt = read(fd, crypt_ftr, sizeof(struct crypt_mnt_ftr))) != sizeof(struct crypt_mnt_ftr)) {
+ SLOGE("Cannot read real block device footer\n");
+ goto errout;
+ }
+
+ if (crypt_ftr->magic != CRYPT_MNT_MAGIC) {
+ SLOGE("Bad magic for real block device %s\n", fname);
+ goto errout;
+ }
+
+ if (crypt_ftr->major_version != CURRENT_MAJOR_VERSION) {
+ SLOGE("Cannot understand major version %d real block device footer; expected %d\n",
+ crypt_ftr->major_version, CURRENT_MAJOR_VERSION);
+ goto errout;
+ }
+
+ // We risk buffer overflows with oversized keys, so we just reject them.
+ // 0-sized keys are problematic (essentially by-passing encryption), and
+ // AES-CBC key wrapping only works for multiples of 16 bytes.
+ if ((crypt_ftr->keysize == 0) || ((crypt_ftr->keysize % 16) != 0) ||
+ (crypt_ftr->keysize > MAX_KEY_LEN)) {
+ SLOGE("Invalid keysize (%u) for block device %s; Must be non-zero, "
+ "divisible by 16, and <= %d\n", crypt_ftr->keysize, fname,
+ MAX_KEY_LEN);
+ goto errout;
+ }
+
+ if (crypt_ftr->minor_version > CURRENT_MINOR_VERSION) {
+ SLOGW("Warning: crypto footer minor version %d, expected <= %d, continuing...\n",
+ crypt_ftr->minor_version, CURRENT_MINOR_VERSION);
+ }
+
+ /* Success! */
+ rc = 0;
+
+errout:
+ close(fd);
+ return rc;
+}
+
+int cryptfs_check_footer()
+{
+ int rc = -1;
+ struct crypt_mnt_ftr crypt_ftr;
+
+ rc = get_crypt_ftr_and_key(&crypt_ftr);
+
+ return rc;
+}
+
+/* Convert a binary key of specified length into an ascii hex string equivalent,
+ * without the leading 0x and with null termination
+ */
+static void convert_key_to_hex_ascii(const unsigned char *master_key,
+ unsigned int keysize, char *master_key_ascii) {
+ unsigned int i, a;
+ unsigned char nibble;
+
+ for (i=0, a=0; i<keysize; i++, a+=2) {
+ /* For each byte, write out two ascii hex digits */
+ nibble = (master_key[i] >> 4) & 0xf;
+ master_key_ascii[a] = nibble + (nibble > 9 ? 0x37 : 0x30);
+
+ nibble = master_key[i] & 0xf;
+ master_key_ascii[a+1] = nibble + (nibble > 9 ? 0x37 : 0x30);
+ }
+
+ /* Add the null termination */
+ master_key_ascii[a] = '\0';
+
+}
+
+static int load_crypto_mapping_table(struct crypt_mnt_ftr *crypt_ftr,
+ const unsigned char *master_key, const char *real_blk_name,
+ const char *name, int fd, const char *extra_params) {
+ alignas(struct dm_ioctl) char buffer[DM_CRYPT_BUF_SIZE];
+ struct dm_ioctl *io;
+ struct dm_target_spec *tgt;
+ char *crypt_params;
+ // We need two ASCII characters to represent each byte, and need space for
+ // the '\0' terminator.
+ char master_key_ascii[MAX_KEY_LEN * 2 + 1];
+ size_t buff_offset;
+ int i;
+
+ io = (struct dm_ioctl *) buffer;
+
+ /* Load the mapping table for this device */
+ tgt = (struct dm_target_spec *) &buffer[sizeof(struct dm_ioctl)];
+
+ ioctl_init(io, DM_CRYPT_BUF_SIZE, name, 0);
+ io->target_count = 1;
+ tgt->status = 0;
+ tgt->sector_start = 0;
+ tgt->length = crypt_ftr->fs_size;
+ crypt_params = buffer + sizeof(struct dm_ioctl) + sizeof(struct dm_target_spec);
+ buff_offset = crypt_params - buffer;
+ SLOGI(
+ "Creating crypto dev \"%s\"; cipher=%s, keysize=%u, real_dev=%s, len=%llu, params=\"%s\"\n",
+ name, crypt_ftr->crypto_type_name, crypt_ftr->keysize, real_blk_name, tgt->length * 512,
+ extra_params);
+
+#ifdef CONFIG_HW_DISK_ENCRYPTION
+ if(is_hw_disk_encryption((char*)crypt_ftr->crypto_type_name)) {
+ strlcpy(tgt->target_type, "req-crypt",DM_MAX_TYPE_NAME);
+ if (is_ice_enabled())
+ convert_key_to_hex_ascii(master_key, sizeof(int), master_key_ascii);
+ else
+ convert_key_to_hex_ascii(master_key, crypt_ftr->keysize, master_key_ascii);
+ }
+ else {
+ convert_key_to_hex_ascii(master_key, crypt_ftr->keysize, master_key_ascii);
+ strlcpy(tgt->target_type, "crypt", DM_MAX_TYPE_NAME);
+ }
+ snprintf(crypt_params, sizeof(buffer) - buff_offset, "%s %s 0 %s 0 %s 0",
+ crypt_ftr->crypto_type_name, master_key_ascii,
+ real_blk_name, extra_params);
+
+ SLOGI("target_type = %s", tgt->target_type);
+ SLOGI("real_blk_name = %s, extra_params = %s", real_blk_name, extra_params);
+#else
+ convert_key_to_hex_ascii(master_key, crypt_ftr->keysize, master_key_ascii);
+ strlcpy(tgt->target_type, "crypt", DM_MAX_TYPE_NAME);
+ snprintf(crypt_params, sizeof(buffer) - buff_offset, "%s %s 0 %s 0 %s",
+ crypt_ftr->crypto_type_name, master_key_ascii, real_blk_name,
+ extra_params);
+#endif
+
+ crypt_params += strlen(crypt_params) + 1;
+ crypt_params = (char *) (((unsigned long)crypt_params + 7) & ~8); /* Align to an 8 byte boundary */
+ tgt->next = crypt_params - buffer;
+
+ for (i = 0; i < TABLE_LOAD_RETRIES; i++) {
+ if (! ioctl(fd, DM_TABLE_LOAD, io)) {
+ break;
+ }
+ usleep(500000);
+ }
+
+ if (i == TABLE_LOAD_RETRIES) {
+ /* We failed to load the table, return an error */
+ return -1;
+ } else {
+ return i + 1;
+ }
+}
+
+static int get_dm_crypt_version(int fd, const char *name, int *version)
+{
+ char buffer[DM_CRYPT_BUF_SIZE];
+ struct dm_ioctl *io;
+ struct dm_target_versions *v;
+
+ io = (struct dm_ioctl *) buffer;
+
+ ioctl_init(io, DM_CRYPT_BUF_SIZE, name, 0);
+
+ if (ioctl(fd, DM_LIST_VERSIONS, io)) {
+ return -1;
+ }
+
+ /* Iterate over the returned versions, looking for name of "crypt".
+ * When found, get and return the version.
+ */
+ v = (struct dm_target_versions *) &buffer[sizeof(struct dm_ioctl)];
+ while (v->next) {
+#ifdef CONFIG_HW_DISK_ENCRYPTION
+ if (! strcmp(v->name, "crypt") || ! strcmp(v->name, "req-crypt")) {
+#else
+ if (! strcmp(v->name, "crypt")) {
+#endif
+ /* We found the crypt driver, return the version, and get out */
+ version[0] = v->version[0];
+ version[1] = v->version[1];
+ version[2] = v->version[2];
+ return 0;
+ }
+ v = (struct dm_target_versions *)(((char *)v) + v->next);
+ }
+
+ return -1;
+}
+
+#ifndef CONFIG_HW_DISK_ENCRYPTION
+static std::string extra_params_as_string(const std::vector<std::string>& extra_params_vec) {
+ if (extra_params_vec.empty()) return "";
+ char temp[10];
+ snprintf(temp, sizeof(temp), "%zd", extra_params_vec.size());
+ std::string extra_params = temp; //std::to_string(extra_params_vec.size());
+ for (const auto& p : extra_params_vec) {
+ extra_params.append(" ");
+ extra_params.append(p);
+ }
+ return extra_params;
+}
+#endif
+
+static int create_crypto_blk_dev(struct crypt_mnt_ftr* crypt_ftr, const unsigned char* master_key,
+ const char* real_blk_name, char* crypto_blk_name, const char* name,
+ uint32_t flags) {
+ char buffer[DM_CRYPT_BUF_SIZE];
+ struct dm_ioctl* io;
+ unsigned int minor;
+ int fd = 0;
+ int err;
+ int retval = -1;
+ int version[3];
+ int load_count;
+#ifdef CONFIG_HW_DISK_ENCRYPTION
+ char encrypted_state[PROPERTY_VALUE_MAX] = {0};
+ char progress[PROPERTY_VALUE_MAX] = {0};
+ const char *extra_params;
+#else
+ std::vector<std::string> extra_params_vec;
+#endif
+
+ if ((fd = open("/dev/device-mapper", O_RDWR | O_CLOEXEC)) < 0) {
+ SLOGE("Cannot open device-mapper\n");
+ goto errout;
+ }
+
+ io = (struct dm_ioctl*)buffer;
+
+ ioctl_init(io, DM_CRYPT_BUF_SIZE, name, 0);
+ err = ioctl(fd, DM_DEV_CREATE, io);
+ if (err) {
+ SLOGE("Cannot create dm-crypt device %s: %s\n", name, strerror(errno));
+ goto errout;
+ }
+
+ /* Get the device status, in particular, the name of it's device file */
+ ioctl_init(io, DM_CRYPT_BUF_SIZE, name, 0);
+ if (ioctl(fd, DM_DEV_STATUS, io)) {
+ SLOGE("Cannot retrieve dm-crypt device status\n");
+ goto errout;
+ }
+ minor = (io->dev & 0xff) | ((io->dev >> 12) & 0xfff00);
+ snprintf(crypto_blk_name, MAXPATHLEN, "/dev/block/dm-%u", minor);
+
+#ifdef CONFIG_HW_DISK_ENCRYPTION
+ if(is_hw_disk_encryption((char*)crypt_ftr->crypto_type_name)) {
+ /* Set fde_enabled if either FDE completed or in-progress */
+ property_get("ro.crypto.state", encrypted_state, ""); /* FDE completed */
+ property_get("vold.encrypt_progress", progress, ""); /* FDE in progress */
+ if (!strcmp(encrypted_state, "encrypted") || strcmp(progress, "")) {
+ if (is_ice_enabled()) {
+ if (flags & CREATE_CRYPTO_BLK_DEV_FLAGS_ALLOW_ENCRYPT_OVERRIDE)
+ extra_params = "fde_enabled ice allow_encrypt_override";
+ else
+ extra_params = "fde_enabled ice";
+ } else {
+ if (flags & CREATE_CRYPTO_BLK_DEV_FLAGS_ALLOW_ENCRYPT_OVERRIDE)
+ extra_params = "fde_enabled allow_encrypt_override";
+ else
+ extra_params = "fde_enabled";
+ }
+ } else {
+ if (flags & CREATE_CRYPTO_BLK_DEV_FLAGS_ALLOW_ENCRYPT_OVERRIDE)
+ extra_params = "fde_enabled allow_encrypt_override";
+ else
+ extra_params = "fde_enabled";
+ }
+ } else {
+ extra_params = "";
+ if (! get_dm_crypt_version(fd, name, version)) {
+ /* Support for allow_discards was added in version 1.11.0 */
+ if ((version[0] >= 2) || ((version[0] == 1) && (version[1] >= 11))) {
+ if (flags & CREATE_CRYPTO_BLK_DEV_FLAGS_ALLOW_ENCRYPT_OVERRIDE)
+ extra_params = "2 allow_discards allow_encrypt_override";
+ else
+ extra_params = "1 allow_discards";
+ SLOGI("Enabling support for allow_discards in dmcrypt.\n");
+ }
+ }
+ }
+ load_count = load_crypto_mapping_table(crypt_ftr, master_key, real_blk_name, name, fd,
+ extra_params);
+#else
+ if (!get_dm_crypt_version(fd, name, version)) {
+ /* Support for allow_discards was added in version 1.11.0 */
+ if ((version[0] >= 2) || ((version[0] == 1) && (version[1] >= 11))) {
+ extra_params_vec.push_back(std::string("allow_discards")); // Used to be extra_params_vec.emplace_back("allow_discards"); but this won't compile in 5.1 trees
+ }
+ }
+ if (flags & CREATE_CRYPTO_BLK_DEV_FLAGS_ALLOW_ENCRYPT_OVERRIDE) {
+ extra_params_vec.push_back(std::string("allow_encrypt_override")); // Used to be extra_params_vec.emplace_back("allow_encrypt_override"); but this won't compile in 5.1 trees
+ }
+ load_count = load_crypto_mapping_table(crypt_ftr, master_key, real_blk_name, name, fd,
+ extra_params_as_string(extra_params_vec).c_str());
+#endif
+ if (load_count < 0) {
+ SLOGE("Cannot load dm-crypt mapping table.\n");
+ goto errout;
+ } else if (load_count > 1) {
+ SLOGI("Took %d tries to load dmcrypt table.\n", load_count);
+ }
+
+ /* Resume this device to activate it */
+ ioctl_init(io, DM_CRYPT_BUF_SIZE, name, 0);
+
+ if (ioctl(fd, DM_DEV_SUSPEND, io)) {
+ SLOGE("Cannot resume the dm-crypt device\n");
+ goto errout;
+ }
+
+ /* We made it here with no errors. Woot! */
+ retval = 0;
+
+errout:
+ close(fd); /* If fd is <0 from a failed open call, it's safe to just ignore the close error */
+
+ return retval;
+}
+
+int delete_crypto_blk_dev(const char *name)
+{
+ int fd;
+ char buffer[DM_CRYPT_BUF_SIZE];
+ struct dm_ioctl *io;
+ int retval = -1;
+
+ if ((fd = open("/dev/device-mapper", O_RDWR|O_CLOEXEC)) < 0 ) {
+ SLOGE("Cannot open device-mapper\n");
+ goto errout;
+ }
+
+ io = (struct dm_ioctl *) buffer;
+
+ ioctl_init(io, DM_CRYPT_BUF_SIZE, name, 0);
+ if (ioctl(fd, DM_DEV_REMOVE, io)) {
+ SLOGE("Cannot remove dm-crypt device\n");
+ goto errout;
+ }
+
+ /* We made it here with no errors. Woot! */
+ retval = 0;
+
+errout:
+ close(fd); /* If fd is <0 from a failed open call, it's safe to just ignore the close error */
+
+ return retval;
+
+}
+
+static int pbkdf2(const char *passwd, const unsigned char *salt,
+ unsigned char *ikey, void *params UNUSED)
+{
+ SLOGI("Using pbkdf2 for cryptfs KDF\n");
+
+ /* Turn the password into a key and IV that can decrypt the master key */
+ return PKCS5_PBKDF2_HMAC_SHA1(passwd, strlen(passwd), salt, SALT_LEN,
+ HASH_COUNT, INTERMEDIATE_BUF_SIZE,
+ ikey) != 1;
+}
+
+static int scrypt(const char *passwd, const unsigned char *salt,
+ unsigned char *ikey, void *params)
+{
+ SLOGI("Using scrypt for cryptfs KDF\n");
+
+ struct crypt_mnt_ftr *ftr = (struct crypt_mnt_ftr *) params;
+
+ int N = 1 << ftr->N_factor;
+ int r = 1 << ftr->r_factor;
+ int p = 1 << ftr->p_factor;
+
+ /* Turn the password into a key and IV that can decrypt the master key */
+ crypto_scrypt((const uint8_t*)passwd, strlen(passwd),
+ salt, SALT_LEN, N, r, p, ikey,
+ INTERMEDIATE_BUF_SIZE);
+
+ return 0;
+}
+
+static int scrypt_keymaster(const char *passwd, const unsigned char *salt,
+ unsigned char *ikey, void *params)
+{
+ SLOGI("Using scrypt with keymaster for cryptfs KDF\n");
+
+ int rc;
+ size_t signature_size;
+ unsigned char* signature;
+ struct crypt_mnt_ftr *ftr = (struct crypt_mnt_ftr *) params;
+
+ int N = 1 << ftr->N_factor;
+ int r = 1 << ftr->r_factor;
+ int p = 1 << ftr->p_factor;
+
+ rc = crypto_scrypt((const uint8_t*)passwd, strlen(passwd),
+ salt, SALT_LEN, N, r, p, ikey,
+ INTERMEDIATE_BUF_SIZE);
+
+ if (rc) {
+ SLOGE("scrypt failed");
+ return -1;
+ }
+
+ if (keymaster_sign_object(ftr, ikey, INTERMEDIATE_BUF_SIZE,
+ &signature, &signature_size)) {
+ SLOGE("Keymaster signing failed");
+ return -1;
+ }
+
+ rc = crypto_scrypt(signature, signature_size, salt, SALT_LEN,
+ N, r, p, ikey, INTERMEDIATE_BUF_SIZE);
+ free(signature);
+
+ if (rc) {
+ SLOGE("scrypt failed");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int decrypt_master_key_aux(const char *passwd, unsigned char *salt,
+ const unsigned char *encrypted_master_key,
+ size_t keysize,
+ unsigned char *decrypted_master_key,
+ kdf_func kdf, void *kdf_params,
+ unsigned char** intermediate_key,
+ size_t* intermediate_key_size)
+{
+ unsigned char ikey[INTERMEDIATE_BUF_SIZE] = { 0 };
+ EVP_CIPHER_CTX d_ctx;
+ int decrypted_len, final_len;
+
+ /* Turn the password into an intermediate key and IV that can decrypt the
+ master key */
+ if (kdf(passwd, salt, ikey, kdf_params)) {
+ SLOGE("kdf failed");
+ return -1;
+ }
+
+ /* Initialize the decryption engine */
+ EVP_CIPHER_CTX_init(&d_ctx);
+ if (! EVP_DecryptInit_ex(&d_ctx, EVP_aes_128_cbc(), NULL, ikey, ikey+INTERMEDIATE_KEY_LEN_BYTES)) {
+ return -1;
+ }
+ EVP_CIPHER_CTX_set_padding(&d_ctx, 0); /* Turn off padding as our data is block aligned */
+ /* Decrypt the master key */
+ if (! EVP_DecryptUpdate(&d_ctx, decrypted_master_key, &decrypted_len,
+ encrypted_master_key, keysize)) {
+ return -1;
+ }
+ if (! EVP_DecryptFinal_ex(&d_ctx, decrypted_master_key + decrypted_len, &final_len)) {
+ return -1;
+ }
+
+ if (decrypted_len + final_len != static_cast<int>(keysize)) {
+ return -1;
+ }
+
+ /* Copy intermediate key if needed by params */
+ if (intermediate_key && intermediate_key_size) {
+ *intermediate_key = (unsigned char*) malloc(INTERMEDIATE_KEY_LEN_BYTES);
+ if (*intermediate_key) {
+ memcpy(*intermediate_key, ikey, INTERMEDIATE_KEY_LEN_BYTES);
+ *intermediate_key_size = INTERMEDIATE_KEY_LEN_BYTES;
+ }
+ }
+
+ EVP_CIPHER_CTX_cleanup(&d_ctx);
+
+ return 0;
+}
+
+static void get_kdf_func(struct crypt_mnt_ftr *ftr, kdf_func *kdf, void** kdf_params)
+{
+ if (ftr->kdf_type == KDF_SCRYPT_KEYMASTER) {
+ *kdf = scrypt_keymaster;
+ *kdf_params = ftr;
+ } else if (ftr->kdf_type == KDF_SCRYPT) {
+ *kdf = scrypt;
+ *kdf_params = ftr;
+ } else {
+ *kdf = pbkdf2;
+ *kdf_params = NULL;
+ }
+}
+
+static int decrypt_master_key(const char *passwd, unsigned char *decrypted_master_key,
+ struct crypt_mnt_ftr *crypt_ftr,
+ unsigned char** intermediate_key,
+ size_t* intermediate_key_size)
+{
+ kdf_func kdf;
+ void *kdf_params;
+ int ret;
+
+ get_kdf_func(crypt_ftr, &kdf, &kdf_params);
+ ret = decrypt_master_key_aux(passwd, crypt_ftr->salt, crypt_ftr->master_key,
+ crypt_ftr->keysize,
+ decrypted_master_key, kdf, kdf_params,
+ intermediate_key, intermediate_key_size);
+ if (ret != 0) {
+ SLOGW("failure decrypting master key");
+ }
+
+ return ret;
+}
+
+#ifdef CONFIG_HW_DISK_ENCRYPTION
+static int test_mount_hw_encrypted_fs(struct crypt_mnt_ftr* crypt_ftr,
+ const char *passwd, const char *mount_point, const char *label)
+{
+ /* Allocate enough space for a 256 bit key, but we may use less */
+ unsigned char decrypted_master_key[32];
+ char crypto_blkdev[MAXPATHLEN];
+ //char real_blkdev[MAXPATHLEN];
+ unsigned int orig_failed_decrypt_count;
+ int rc = 0;
+
+ SLOGD("crypt_ftr->fs_size = %lld\n", crypt_ftr->fs_size);
+ orig_failed_decrypt_count = crypt_ftr->failed_decrypt_count;
+
+ //fs_mgr_get_crypt_info(fstab_default, 0, real_blkdev, sizeof(real_blkdev));
+
+ int key_index = 0;
+ if(is_hw_disk_encryption((char*)crypt_ftr->crypto_type_name)) {
+ key_index = verify_and_update_hw_fde_passwd(passwd, crypt_ftr);
+ if (key_index < 0) {
+ rc = -1;
+ goto errout;
+ }
+ else {
+ if (is_ice_enabled()) {
+#ifndef CONFIG_HW_DISK_ENCRYPT_PERF
+ if (create_crypto_blk_dev(crypt_ftr, (unsigned char*)&key_index,
+ real_blkdev, crypto_blkdev, label, 0)) {
+ SLOGE("Error creating decrypted block device");
+ rc = -1;
+ goto errout;
+ }
+#endif
+ } else {
+ if (create_crypto_blk_dev(crypt_ftr, decrypted_master_key,
+ real_blkdev, crypto_blkdev, label, 0)) {
+ SLOGE("Error creating decrypted block device");
+ rc = -1;
+ goto errout;
+ }
+ }
+ }
+ }
+
+ if (rc == 0) {
+ /* Save the name of the crypto block device
+ * so we can mount it when restarting the framework. */
+#ifdef CONFIG_HW_DISK_ENCRYPT_PERF
+ if (!is_ice_enabled())
+#endif
+ property_set("ro.crypto.fs_crypto_blkdev", crypto_blkdev);
+ master_key_saved = 1;
+ }
+
+ errout:
+ return rc;
+}
+#endif
+
+static int try_mount_multiple_fs(const char *crypto_blkdev,
+ const char *mount_point,
+ const char *file_system)
+{
+ if (!mount(crypto_blkdev, mount_point, file_system, 0, NULL))
+ return 0;
+ if (strcmp(file_system, "ext4") &&
+ !mount(crypto_blkdev, mount_point, "ext4", 0, NULL))
+ return 0;
+ if (strcmp(file_system, "f2fs") &&
+ !mount(crypto_blkdev, mount_point, "f2fs", 0, NULL))
+ return 0;
+ return 1;
+}
+
+static int test_mount_encrypted_fs(struct crypt_mnt_ftr* crypt_ftr,
+ const char *passwd, const char *mount_point, const char *label)
+{
+ unsigned char decrypted_master_key[MAX_KEY_LEN];
+ char crypto_blkdev[MAXPATHLEN];
+ //char real_blkdev[MAXPATHLEN];
+ char tmp_mount_point[64];
+ unsigned int orig_failed_decrypt_count;
+ int rc;
+ int use_keymaster = 0;
+ unsigned char* intermediate_key = 0;
+ size_t intermediate_key_size = 0;
+ int N = 1 << crypt_ftr->N_factor;
+ int r = 1 << crypt_ftr->r_factor;
+ int p = 1 << crypt_ftr->p_factor;
+
+ SLOGD("crypt_ftr->fs_size = %lld\n", crypt_ftr->fs_size);
+ orig_failed_decrypt_count = crypt_ftr->failed_decrypt_count;
+
+ if (! (crypt_ftr->flags & CRYPT_MNT_KEY_UNENCRYPTED) ) {
+ if (decrypt_master_key(passwd, decrypted_master_key, crypt_ftr,
+ &intermediate_key, &intermediate_key_size)) {
+ SLOGE("Failed to decrypt master key\n");
+ rc = -1;
+ goto errout;
+ }
+ }
+
+ //fs_mgr_get_crypt_info(fstab_default, 0, real_blkdev, sizeof(real_blkdev));
+
+ // Create crypto block device - all (non fatal) code paths
+ // need it
+ if (create_crypto_blk_dev(crypt_ftr, decrypted_master_key, real_blkdev, crypto_blkdev, label, 0)) {
+ SLOGE("Error creating decrypted block device\n");
+ rc = -1;
+ goto errout;
+ }
+
+ /* Work out if the problem is the password or the data */
+ unsigned char scrypted_intermediate_key[sizeof(crypt_ftr->
+ scrypted_intermediate_key)];
+
+ rc = crypto_scrypt(intermediate_key, intermediate_key_size,
+ crypt_ftr->salt, sizeof(crypt_ftr->salt),
+ N, r, p, scrypted_intermediate_key,
+ sizeof(scrypted_intermediate_key));
+
+ // Does the key match the crypto footer?
+ if (rc == 0 && memcmp(scrypted_intermediate_key,
+ crypt_ftr->scrypted_intermediate_key,
+ sizeof(scrypted_intermediate_key)) == 0) {
+ SLOGI("Password matches");
+ rc = 0;
+ } else {
+ /* Try mounting the file system anyway, just in case the problem's with
+ * the footer, not the key. */
+ snprintf(tmp_mount_point, sizeof(tmp_mount_point), "%s/tmp_mnt",
+ mount_point);
+ mkdir(tmp_mount_point, 0755);
+ if (try_mount_multiple_fs(crypto_blkdev, tmp_mount_point, file_system)) {
+ SLOGE("Error temp mounting decrypted block device\n");
+ delete_crypto_blk_dev(label);
+
+ rc = -1;
+ } else {
+ /* Success! */
+ SLOGI("Password did not match but decrypted drive mounted - continue");
+ umount(tmp_mount_point);
+ rc = 0;
+ }
+ }
+
+ if (rc == 0) {
+ /* Save the name of the crypto block device
+ * so we can mount it when restarting the framework. */
+ property_set("ro.crypto.fs_crypto_blkdev", crypto_blkdev);
+
+ /* Also save a the master key so we can reencrypted the key
+ * the key when we want to change the password on it. */
+ memcpy(saved_master_key, decrypted_master_key, crypt_ftr->keysize);
+ saved_mount_point = strdup(mount_point);
+ master_key_saved = 1;
+ SLOGD("%s(): Master key saved\n", __FUNCTION__);
+ rc = 0;
+ }
+
+ errout:
+ if (intermediate_key) {
+ memset(intermediate_key, 0, intermediate_key_size);
+ free(intermediate_key);
+ }
+ return rc;
+}
+
+/*
+ * Called by vold when it's asked to mount an encrypted external
+ * storage volume. The incoming partition has no crypto header/footer,
+ * as any metadata is been stored in a separate, small partition. We
+ * assume it must be using our same crypt type and keysize.
+ *
+ * out_crypto_blkdev must be MAXPATHLEN.
+ */
+int cryptfs_setup_ext_volume(const char* label, const char* real_blkdev,
+ const unsigned char* key, int keysize, char* out_crypto_blkdev) {
+ int fd = open(real_blkdev, O_RDONLY|O_CLOEXEC);
+ if (fd == -1) {
+ SLOGE("Failed to open %s: %s", real_blkdev, strerror(errno));
+ return -1;
+ }
+
+ unsigned long nr_sec = 0;
+ get_blkdev_size(fd, &nr_sec);
+ close(fd);
+
+ if (nr_sec == 0) {
+ SLOGE("Failed to get size of %s: %s", real_blkdev, strerror(errno));
+ return -1;
+ }
+
+ struct crypt_mnt_ftr ext_crypt_ftr;
+ memset(&ext_crypt_ftr, 0, sizeof(ext_crypt_ftr));
+ ext_crypt_ftr.fs_size = nr_sec;
+ ext_crypt_ftr.keysize = cryptfs_get_keysize();
+ strlcpy((char*) ext_crypt_ftr.crypto_type_name, cryptfs_get_crypto_name(),
+ MAX_CRYPTO_TYPE_NAME_LEN);
+ uint32_t flags = 0;
+ /*if (e4crypt_is_native() &&
+ android::base::GetBoolProperty("ro.crypto.allow_encrypt_override", false))
+ flags |= CREATE_CRYPTO_BLK_DEV_FLAGS_ALLOW_ENCRYPT_OVERRIDE;*/
+
+ return create_crypto_blk_dev(&ext_crypt_ftr, key, real_blkdev, out_crypto_blkdev, label, flags);
+}
+
+/*
+ * Called by vold when it's asked to unmount an encrypted external
+ * storage volume.
+ */
+int cryptfs_revert_ext_volume(const char* label) {
+ return delete_crypto_blk_dev(label);
+}
+
+int check_unmounted_and_get_ftr(struct crypt_mnt_ftr* crypt_ftr)
+{
+ char encrypted_state[PROPERTY_VALUE_MAX];
+ property_get("ro.crypto.state", encrypted_state, "");
+ if ( master_key_saved || strcmp(encrypted_state, "encrypted") ) {
+ SLOGE("encrypted fs already validated or not running with encryption,"
+ " aborting");
+ return -1;
+ }
+
+ if (get_crypt_ftr_and_key(crypt_ftr)) {
+ SLOGE("Error getting crypt footer and key");
+ return -1;
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_HW_DISK_ENCRYPTION
+int cryptfs_check_passwd_hw(const char* passwd)
+{
+ struct crypt_mnt_ftr crypt_ftr;
+ int rc;
+ unsigned char master_key[KEY_LEN_BYTES];
+ /* get key */
+ if (get_crypt_ftr_and_key(&crypt_ftr)) {
+ SLOGE("Error getting crypt footer and key");
+ return -1;
+ }
+
+ /*
+ * in case of manual encryption (from GUI), the encryption is done with
+ * default password
+ */
+ if (crypt_ftr.flags & CRYPT_FORCE_COMPLETE) {
+ /* compare scrypted_intermediate_key with stored scrypted_intermediate_key
+ * which was created with actual password before reboot.
+ */
+ rc = cryptfs_get_master_key(&crypt_ftr, passwd, master_key);
+ if (rc) {
+ SLOGE("password doesn't match");
+ return rc;
+ }
+
+ rc = test_mount_hw_encrypted_fs(&crypt_ftr, DEFAULT_PASSWORD,
+ DATA_MNT_POINT, CRYPTO_BLOCK_DEVICE);
+
+ if (rc) {
+ SLOGE("Default password did not match on reboot encryption");
+ return rc;
+ }
+ } else {
+ rc = test_mount_hw_encrypted_fs(&crypt_ftr, passwd,
+ DATA_MNT_POINT, CRYPTO_BLOCK_DEVICE);
+ SLOGE("test mount returned %i\n", rc);
+ }
+
+ return rc;
+}
+#endif
+
+int cryptfs_check_passwd(const char *passwd)
+{
+ /*if (e4crypt_is_native()) {
+ SLOGE("cryptfs_check_passwd not valid for file encryption");
+ return -1;
+ }*/
+
+ struct crypt_mnt_ftr crypt_ftr;
+ int rc;
+
+ rc = check_unmounted_and_get_ftr(&crypt_ftr);
+ if (rc) {
+ SLOGE("Could not get footer");
+ return rc;
+ }
+
+#ifdef CONFIG_HW_DISK_ENCRYPTION
+ if (is_hw_disk_encryption((char*)crypt_ftr.crypto_type_name))
+ return cryptfs_check_passwd_hw(passwd);
+#endif
+
+ rc = test_mount_encrypted_fs(&crypt_ftr, passwd,
+ DATA_MNT_POINT, CRYPTO_BLOCK_DEVICE);
+
+ if (rc) {
+ SLOGE("Password did not match");
+ return rc;
+ }
+
+ if (crypt_ftr.flags & CRYPT_FORCE_COMPLETE) {
+ // Here we have a default actual password but a real password
+ // we must test against the scrypted value
+ // First, we must delete the crypto block device that
+ // test_mount_encrypted_fs leaves behind as a side effect
+ delete_crypto_blk_dev(CRYPTO_BLOCK_DEVICE);
+ rc = test_mount_encrypted_fs(&crypt_ftr, DEFAULT_PASSWORD,
+ DATA_MNT_POINT, CRYPTO_BLOCK_DEVICE);
+ if (rc) {
+ SLOGE("Default password did not match on reboot encryption");
+ return rc;
+ }
+ }
+
+ return rc;
+}
+
+int cryptfs_verify_passwd(const char *passwd)
+{
+ struct crypt_mnt_ftr crypt_ftr;
+ unsigned char decrypted_master_key[MAX_KEY_LEN];
+ char encrypted_state[PROPERTY_VALUE_MAX];
+ int rc;
+
+ property_get("ro.crypto.state", encrypted_state, "");
+ if (strcmp(encrypted_state, "encrypted") ) {
+ SLOGE("device not encrypted, aborting");
+ return -2;
+ }
+
+ if (!master_key_saved) {
+ SLOGE("encrypted fs not yet mounted, aborting");
+ return -1;
+ }
+
+ if (!saved_mount_point) {
+ SLOGE("encrypted fs failed to save mount point, aborting");
+ return -1;
+ }
+
+ if (get_crypt_ftr_and_key(&crypt_ftr)) {
+ SLOGE("Error getting crypt footer and key\n");
+ return -1;
+ }
+
+ if (crypt_ftr.flags & CRYPT_MNT_KEY_UNENCRYPTED) {
+ /* If the device has no password, then just say the password is valid */
+ rc = 0;
+ } else {
+#ifdef CONFIG_HW_DISK_ENCRYPTION
+ if(is_hw_disk_encryption((char*)crypt_ftr.crypto_type_name)) {
+ if (verify_hw_fde_passwd(passwd, &crypt_ftr) >= 0)
+ rc = 0;
+ else
+ rc = -1;
+ } else {
+ decrypt_master_key(passwd, decrypted_master_key, &crypt_ftr, 0, 0);
+ if (!memcmp(decrypted_master_key, saved_master_key, crypt_ftr.keysize)) {
+ /* They match, the password is correct */
+ rc = 0;
+ } else {
+ /* If incorrect, sleep for a bit to prevent dictionary attacks */
+ sleep(1);
+ rc = 1;
+ }
+ }
+#else
+ decrypt_master_key(passwd, decrypted_master_key, &crypt_ftr, 0, 0);
+ if (!memcmp(decrypted_master_key, saved_master_key, crypt_ftr.keysize)) {
+ /* They match, the password is correct */
+ rc = 0;
+ } else {
+ /* If incorrect, sleep for a bit to prevent dictionary attacks */
+ sleep(1);
+ rc = 1;
+ }
+#endif
+ }
+
+ return rc;
+}
+
+/* Returns type of the password, default, pattern, pin or password.
+ */
+int cryptfs_get_password_type(void)
+{
+ struct crypt_mnt_ftr crypt_ftr;
+
+ if (get_crypt_ftr_and_key(&crypt_ftr)) {
+ SLOGE("Error getting crypt footer and key\n");
+ return -1;
+ }
+
+ if (crypt_ftr.flags & CRYPT_INCONSISTENT_STATE) {
+ return -1;
+ }
+
+ return crypt_ftr.crypt_type;
+}
+
+int cryptfs_get_master_key(struct crypt_mnt_ftr* ftr, const char* password,
+ unsigned char* master_key)
+{
+ int rc;
+
+ unsigned char* intermediate_key = 0;
+ size_t intermediate_key_size = 0;
+
+ if (password == 0 || *password == 0) {
+ password = DEFAULT_PASSWORD;
+ }
+
+ rc = decrypt_master_key(password, master_key, ftr, &intermediate_key,
+ &intermediate_key_size);
+
+ if (rc) {
+ SLOGE("Can't calculate intermediate key");
+ return rc;
+ }
+
+ int N = 1 << ftr->N_factor;
+ int r = 1 << ftr->r_factor;
+ int p = 1 << ftr->p_factor;
+
+ unsigned char scrypted_intermediate_key[sizeof(ftr->scrypted_intermediate_key)];
+
+ rc = crypto_scrypt(intermediate_key, intermediate_key_size,
+ ftr->salt, sizeof(ftr->salt), N, r, p,
+ scrypted_intermediate_key,
+ sizeof(scrypted_intermediate_key));
+
+ free(intermediate_key);
+
+ if (rc) {
+ SLOGE("Can't scrypt intermediate key");
+ return rc;
+ }
+
+ return memcmp(scrypted_intermediate_key, ftr->scrypted_intermediate_key,
+ intermediate_key_size);
+}
+
diff --git a/crypto/fde/cryptfs.h b/crypto/fde/cryptfs.h
new file mode 100644
index 0000000..c67113a
--- /dev/null
+++ b/crypto/fde/cryptfs.h
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_VOLD_CRYPTFS_H
+#define ANDROID_VOLD_CRYPTFS_H
+
+/* This structure starts 16,384 bytes before the end of a hardware
+ * partition that is encrypted, or in a separate partition. It's location
+ * is specified by a property set in init.<device>.rc.
+ * The structure allocates 48 bytes for a key, but the real key size is
+ * specified in the struct. Currently, the code is hardcoded to use 128
+ * bit keys.
+ * The fields after salt are only valid in rev 1.1 and later stuctures.
+ * Obviously, the filesystem does not include the last 16 kbytes
+ * of the partition if the crypt_mnt_ftr lives at the end of the
+ * partition.
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <cutils/properties.h>
+
+/* The current cryptfs version */
+#define CURRENT_MAJOR_VERSION 1
+#define CURRENT_MINOR_VERSION 3
+
+#define CRYPT_FOOTER_OFFSET 0x4000
+#define CRYPT_FOOTER_TO_PERSIST_OFFSET 0x1000
+#define CRYPT_PERSIST_DATA_SIZE 0x1000
+
+#define MAX_CRYPTO_TYPE_NAME_LEN 64
+
+#define MAX_KEY_LEN 48