decrypt: AOSP 10 requires the use of fscrypt
fscrypt aosp doc: https://source.android.com/security/encryption/file-based
kernel fscrypt doc: https://www.kernel.org/doc/html/v4.18/filesystems/fscrypt.html
This commit implements the ability for TWRP to use fscrypt to decrypt
files on the fscrypt implementation. It has been implemented mostly
in a new successor library to e4crypt called libtwrpfscrypt. Most of the
code was ported from AOSP vold.
Notable updates include:
- updated policy storage by libtar
- lookup of fbe policies by libtwrpfscrypt
- threaded keystore operations
Big thanks to Dees_Troy for the initial trailblazing
of encryption in TWRP.
Change-Id: I69cd2eba3693a9914e00213d4943229635d0cdae
diff --git a/crypto/ext4crypt/Android.mk b/crypto/ext4crypt/Android.mk
index c8283dd..955bd3b 100755
--- a/crypto/ext4crypt/Android.mk
+++ b/crypto/ext4crypt/Android.mk
@@ -25,7 +25,6 @@
LOCAL_CFLAGS += -DHAVE_GATEKEEPER1
ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 29; echo $$?),0)
LOCAL_SHARED_LIBRARIES += android.hardware.confirmationui@1.0
- # LOCAL_CFLAGS += -DUSE_
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)
diff --git a/crypto/fde/Android.mk b/crypto/fde/Android.mk
old mode 100644
new mode 100755
index fcdd564..4fd8b0b
--- a/crypto/fde/Android.mk
+++ b/crypto/fde/Android.mk
@@ -17,7 +17,13 @@
ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 26; echo $$?),0)
#8.0 or higher
LOCAL_C_INCLUDES += external/boringssl/src/include
- LOCAL_SHARED_LIBRARIES += libselinux libc libc++ libbase libcrypto libcutils libkeymaster_messages libhardware libprotobuf-cpp-lite libe4crypt android.hardware.keymaster@3.0 libkeystore_binder libhidlbase libutils libbinder
+ 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
@@ -72,7 +78,13 @@
ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 26; echo $$?),0)
#8.0 or higher
LOCAL_C_INCLUDES += external/boringssl/src/include
- LOCAL_SHARED_LIBRARIES += libselinux libc libc++ libbase libcrypto libcutils libkeymaster_messages libhardware libprotobuf-cpp-lite libe4crypt android.hardware.keymaster@3.0 libkeystore_binder libhidlbase libutils libbinder
+ 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
diff --git a/crypto/fscrypt/Android.mk b/crypto/fscrypt/Android.mk
new file mode 100755
index 0000000..8000d5e
--- /dev/null
+++ b/crypto/fscrypt/Android.mk
@@ -0,0 +1,82 @@
+LOCAL_PATH := $(call my-dir)
+ifeq ($(TW_INCLUDE_CRYPTO), true)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libtwrpfscrypt
+LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS := -Wno-unused-variable -Wno-sign-compare -Wno-unused-parameter -Wno-comment -Wno-missing-field-initializers \
+ -DHAVE_LIBKEYUTILS -std=gnu++2a -Wno-macro-redefined -Wno-unused-function
+LOCAL_SRC_FILES := Decrypt.cpp ScryptParameters.cpp Utils.cpp HashPassword.cpp \
+ FsCrypt.cpp KeyUtil.cpp Keymaster.cpp KeyStorage.cpp MetadataCrypt.cpp KeyBuffer.cpp \
+ Process.cpp EncryptInplace.cpp Weaver1.cpp fscrypt_policy.cpp
+LOCAL_SHARED_LIBRARIES := libselinux libc libc++ libext4_utils libbase libcrypto libcutils \
+libkeymaster_messages libhardware libprotobuf-cpp-lite libfscrypt android.hardware.confirmationui@1.0 \
+android.hardware.keymaster@3.0 libkeystore_binder libhidlbase libutils libbinder android.hardware.gatekeeper@1.0 \
+libfs_mgr android.hardware.keymaster@4.0 libkeymaster4support libf2fs_sparseblock libkeystore_parcelables \
+libkeystore_aidl android.hardware.weaver@1.0 libkeyutils liblog libhwbinder libchrome
+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 \
+ system/extras/libfscrypt/include \
+ system/core/fs_mgr/libfs_avb/include/ \
+ system/core/fs_mgr/include_fstab/ \
+ system/core/fs_mgr/include/ \
+ system/core/fs_mgr/libdm/include/ \
+ system/core/fs_mgr/liblp/include/ \
+ system/gsid/include/ \
+ system/core/init/ \
+ system/vold/model \
+ system/vold/ \
+ system/extras/f2fs_utils/
+
+ifneq ($(wildcard hardware/libhardware/include/hardware/keymaster0.h),)
+ LOCAL_CFLAGS += -DTW_CRYPTO_HAVE_KEYMASTERX
+ LOCAL_C_INCLUDES += external/boringssl/src/include
+endif
+
+LOCAL_REQUIRED_MODULES := keystore_auth keystore
+LOCAL_CLANG := true
+include $(BUILD_SHARED_LIBRARY)
+
+
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := twrpfbe
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
+LOCAL_SRC_FILES := main.cpp
+LOCAL_SHARED_LIBRARIES := libtwrpfscrypt
+
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := fscryptpolicyget
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
+LOCAL_SRC_FILES := fscryptpolicyget.cpp
+LOCAL_SHARED_LIBRARIES := libtwrpfscrypt
+LOCAL_LDFLAGS += -Wl,-dynamic-linker,/sbin/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)/sbin
+LOCAL_SRC_FILES := keystore_auth.cpp
+LOCAL_SHARED_LIBRARIES := libc libkeystore_binder libutils libbinder liblog
+LOCAL_CFLAGS += -DUSE_SECURITY_NAMESPACE
+LOCAL_SHARED_LIBRARIES += libkeystore_aidl
+LOCAL_LDFLAGS += -Wl,-dynamic-linker,/sbin/linker64
+
+include $(BUILD_EXECUTABLE)
+
+endif
diff --git a/crypto/fscrypt/Checkpoint.h b/crypto/fscrypt/Checkpoint.h
new file mode 100644
index 0000000..63ead83
--- /dev/null
+++ b/crypto/fscrypt/Checkpoint.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _CHECKPOINT_H
+#define _CHECKPOINT_H
+
+#include <binder/Status.h>
+#include <string>
+
+namespace android {
+namespace vold {
+
+android::binder::Status cp_supportsCheckpoint(bool& result);
+
+android::binder::Status cp_supportsBlockCheckpoint(bool& result);
+
+android::binder::Status cp_supportsFileCheckpoint(bool& result);
+
+android::binder::Status cp_startCheckpoint(int retry);
+
+android::binder::Status cp_commitChanges();
+
+void cp_abortChanges(const std::string& message, bool retry);
+
+bool cp_needsRollback();
+
+bool cp_needsCheckpoint();
+
+android::binder::Status cp_prepareCheckpoint();
+
+android::binder::Status cp_restoreCheckpoint(const std::string& mountPoint, int count = 0);
+
+android::binder::Status cp_markBootAttempt();
+
+} // namespace vold
+} // namespace android
+
+#endif
diff --git a/crypto/fscrypt/Decrypt.cpp b/crypto/fscrypt/Decrypt.cpp
new file mode 100755
index 0000000..b1a43c9
--- /dev/null
+++ b/crypto/fscrypt/Decrypt.cpp
@@ -0,0 +1,1169 @@
+/*
+ * Copyright (C) 2016 - 2020 The TeamWin Recovery Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Decrypt.h"
+#include "FsCrypt.h"
+
+#include <map>
+#include <string>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#ifndef HAVE_LIBKEYUTILS
+#include "key_control.h"
+#else
+#include <keyutils.h>
+#endif
+#include "keystore_client.pb.h"
+#include "Weaver1.h"
+#include "cutils/properties.h"
+
+#include <openssl/sha.h>
+#include <openssl/aes.h>
+#include <openssl/evp.h>
+#include <openssl/rand.h>
+
+#include <dirent.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/types.h>
+#include <fstream>
+#include <future>
+#include <algorithm>
+
+#include <android-base/file.h>
+#include <base/threading/platform_thread.h>
+#include <android/hardware/confirmationui/1.0/types.h>
+#include <android/security/BnConfirmationPromptCallback.h>
+#include <android/security/keystore/IKeystoreService.h>
+#include <android/hardware/gatekeeper/1.0/IGatekeeper.h>
+
+#include <binder/IServiceManager.h>
+#include <binder/IPCThreadState.h>
+#include <hardware/hw_auth_token.h>
+
+#include <keystore/keystore.h>
+#include <keystore/keystore_client.h>
+#include <keystore/keystore_client_impl.h>
+#include <keystore/KeystoreResponse.h>
+#include <keystore/keystore_hidl_support.h>
+#include <keystore/keystore_promises.h>
+#include <keystore/keystore_return_types.h>
+#include <keystore/keymaster_types.h>
+#include <keymasterV4_0/Keymaster.h>
+#include <keystore/OperationResult.h>
+#include "keystore_client.pb.h"
+
+#include <keymasterV4_0/authorization_set.h>
+#include <keymasterV4_0/keymaster_utils.h>
+
+extern "C" {
+#include "crypto_scrypt.h"
+}
+
+#include "fscrypt_policy.h"
+#include "HashPassword.h"
+#include "KeyStorage.h"
+
+using android::security::keystore::IKeystoreService;
+using keystore::KeystoreResponsePromise;
+using keystore::OperationResultPromise;
+using android::security::keymaster::OperationResult;
+
+// 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;
+
+inline std::string hidlVec2String(const ::keystore::hidl_vec<uint8_t>& value) {
+ return std::string(reinterpret_cast<const std::string::value_type*>(&value[0]), value.size());
+}
+
+static bool lookup_ref_key_internal(std::map<userid_t, std::string>& key_map, const uint8_t* policy, userid_t* user_id) {
+ char policy_string_hex[FS_KEY_DESCRIPTOR_SIZE_HEX];
+ char key_map_hex[FS_KEY_DESCRIPTOR_SIZE_HEX];
+ policy_to_hex(policy, policy_string_hex);
+
+ for (std::map<userid_t, std::string>::iterator it=key_map.begin(); it!=key_map.end(); ++it) {
+ policy_to_hex(reinterpret_cast<const uint8_t*>(&it->second[0]), key_map_hex);
+ std::string key_map_hex_string = std::string(key_map_hex);
+ if (key_map_hex_string == policy_string_hex) {
+ *user_id = it->first;
+ return true;
+ }
+ }
+ return false;
+}
+
+extern "C" bool lookup_ref_key(const uint8_t* policy, uint8_t* policy_type) {
+ userid_t user_id = 0;
+ char policy_string_hex[FS_KEY_DESCRIPTOR_SIZE_HEX];
+ char de_raw_ref_hex[FS_KEY_DESCRIPTOR_SIZE_HEX];
+ policy_to_hex(policy, policy_string_hex);
+ policy_to_hex(reinterpret_cast<const uint8_t*>(&de_raw_ref[0]), de_raw_ref_hex);
+ std::string de_raw_ref_hex_string = std::string(de_raw_ref_hex);
+
+ std::string policy_type_string;
+ if (policy_string_hex == de_raw_ref_hex_string) {
+ policy_type_string = "0DK";
+ memcpy(policy_type, policy_type_string.data(), policy_type_string.size());
+ 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
+ policy_type_string = "0CE" + std::to_string(user_id);
+ } else
+ policy_type_string = "0DE" + std::to_string(user_id);
+ memcpy(policy_type, policy_type_string.data(), policy_type_string.size());
+ return true;
+}
+
+extern "C" bool lookup_ref_tar(const uint8_t* policy_type, uint8_t* policy) {
+ std::string policy_type_string = std::string((char *) policy_type);
+ char policy_hex[FS_KEY_DESCRIPTOR_SIZE_HEX];
+ policy_to_hex(policy_type, policy_hex);
+
+ // Current encryption fscrypt policy is v1 (which is stored as version 0e)
+ if (policy_type_string.substr(0,1) != "0") {
+ printf("Unexpected version %c\n", policy_type[0]);
+ return false;
+ }
+
+ if (policy_type_string.substr(1, 2) == "DK") {
+ memcpy(policy, de_raw_ref.data(), de_raw_ref.size());
+ return true;
+ }
+
+ userid_t user_id = atoi(policy_type_string.substr(3, 4).c_str());
+ std::string raw_ref;
+
+ if (policy_type_string.substr(1, 1) == "D") {
+ if (lookup_key_ref(s_de_key_raw_refs, user_id, &raw_ref)) {
+ memcpy(policy, raw_ref.data(), raw_ref.size());
+ } else
+ return false;
+ } else if (policy_type_string.substr(1, 1) == "C") {
+ if (lookup_key_ref(s_ce_key_raw_refs, user_id, &raw_ref)) {
+ memcpy(policy, raw_ref.data(), raw_ref.size());
+ } else
+ return false;
+ } else {
+ printf("unknown policy type '%s'\n", policy_type);
+ return false;
+ }
+ return true;
+}
+
+bool Decrypt_DE() {
+ if (!fscrypt_initialize_systemwide_keys()) { // this deals with the overarching device encryption
+ printf("fscrypt_initialize_systemwide_keys returned fail\n");
+ return false;
+ }
+ if (!fscrypt_init_user0()) {
+ printf("fscrypt_init_user0 returned fail\n");
+ return false;
+ }
+ return true;
+}
+
+// Crappy functions for debugging, please ignore unless you need to debug
+// void output_hex(const std::string& in) {
+// const char *buf = in.data();
+// char hex[in.size() * 2 + 1];
+// unsigned int index;
+// for (index = 0; index < in.size(); index++)
+// sprintf(&hex[2 * index], "%02X", buf[index]);
+// printf("%s", hex);
+// }
+
+// void output_hex(const char* buf, const int size) {
+// char hex[size * 2 + 1];
+// int index;
+// for (index = 0; index < size; index++)
+// sprintf(&hex[2 * index], "%02X", buf[index]);
+// printf("%s", hex);
+// }
+
+// void output_hex(const unsigned char* buf, const int size) {
+// char hex[size * 2 + 1];
+// int index;
+// for (index = 0; index < size; index++)
+// sprintf(&hex[2 * index], "%02X", buf[index]);
+// printf("%s", hex);
+// }
+
+// void output_hex(std::vector<uint8_t>* vec) {
+// char hex[3];
+// unsigned int index;
+// for (index = 0; index < vec->size(); index++) {
+// sprintf(&hex[0], "%02X", vec->at(index));
+// printf("%s", hex);
+// }
+// }
+
+/* An alternative is to use:
+ * sqlite3 /data/system/locksettings.db "SELECT value FROM locksettings WHERE name='sp-handle' AND user=0;"
+ * but we really don't want to include the 1.1MB libsqlite in TWRP. We scan the spblob folder for the
+ * password data file (*.pwd) and get the handle from the filename instead. This is a replacement for
+ * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/LockSettingsService.java#2017
+ * We never use this data as an actual long. We always use it as a string. */
+bool Find_Handle(const std::string& spblob_path, std::string& handle_str) {
+ DIR* dir = opendir(spblob_path.c_str());
+ if (!dir) {
+ printf("Error opening '%s'\n", spblob_path.c_str());
+ return false;
+ }
+
+ struct dirent* de = 0;
+
+ while ((de = readdir(dir)) != 0) {
+ if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
+ continue;
+ size_t len = strlen(de->d_name);
+ if (len <= 4)
+ continue;
+ char* p = de->d_name;
+ p += len - 4;
+ if (strncmp(p, ".pwd", 4) == 0) {
+ handle_str = de->d_name;
+ handle_str = handle_str.substr(0, len - 4);
+ //*handle = strtoull(handle_str.c_str(), 0 , 16);
+ closedir(dir);
+ return true;
+ }
+ }
+ closedir(dir);
+ return false;
+}
+
+/* This is the structure of the data in the password data (*.pwd) file which the structure can be found
+ * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#187 */
+struct password_data_struct {
+ int password_type;
+ unsigned char scryptN;
+ unsigned char scryptR;
+ unsigned char scryptP;
+ int salt_len;
+ void* salt;
+ int handle_len;
+ void* password_handle;
+};
+
+/* C++ replacement for
+ * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#764 */
+bool Get_Password_Data(const std::string& spblob_path, const std::string& handle_str, password_data_struct *pwd) {
+ std::string pwd_file = spblob_path + handle_str + ".pwd";
+ std::string pwd_data;
+ if (!android::base::ReadFileToString(pwd_file, &pwd_data)) {
+ printf("Failed to read '%s'\n", pwd_file.c_str());
+ return false;
+ }
+ // output_hex(pwd_data.data(), pwd_data.size());printf("\n");
+ const int* intptr = (const int*)pwd_data.data();
+ pwd->password_type = *intptr;
+ endianswap(&pwd->password_type);
+ //printf("password type %i\n", pwd->password_type); // 2 was PIN, 1 for pattern, 2 also for password, -1 for default password
+ const unsigned char* byteptr = (const unsigned char*)pwd_data.data() + sizeof(int);
+ pwd->scryptN = *byteptr;
+ byteptr++;
+ pwd->scryptR = *byteptr;
+ byteptr++;
+ pwd->scryptP = *byteptr;
+ byteptr++;
+ intptr = (const int*)byteptr;
+ pwd->salt_len = *intptr;
+ endianswap(&pwd->salt_len);
+ if (pwd->salt_len != 0) {
+ pwd->salt = malloc(pwd->salt_len);
+ if (!pwd->salt) {
+ printf("Get_Password_Data malloc salt\n");
+ return false;
+ }
+ memcpy(pwd->salt, intptr + 1, pwd->salt_len);
+ intptr++;
+ byteptr = (const unsigned char*)intptr;
+ byteptr += pwd->salt_len;
+ } else {
+ printf("Get_Password_Data salt_len is 0\n");
+ return false;
+ }
+ intptr = (const int*)byteptr;
+ pwd->handle_len = *intptr;
+ endianswap(&pwd->handle_len);
+ if (pwd->handle_len != 0) {
+ pwd->password_handle = malloc(pwd->handle_len);
+ if (!pwd->password_handle) {
+ printf("Get_Password_Data malloc password_handle\n");
+ return false;
+ }
+ memcpy(pwd->password_handle, intptr + 1, pwd->handle_len);
+ } else {
+ printf("Get_Password_Data handle_len is 0\n");
+ // Not an error if using weaver
+ }
+ return true;
+}
+
+/* C++ replacement for
+ * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#765
+ * called here
+ * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#1050 */
+bool Get_Password_Token(const password_data_struct *pwd, const std::string& Password, unsigned char* password_token) {
+ if (!password_token) {
+ printf("password_token is null\n");
+ return false;
+ }
+ unsigned int N = 1 << pwd->scryptN;
+ unsigned int r = 1 << pwd->scryptR;
+ unsigned int p = 1 << pwd->scryptP;
+ //printf("N %i r %i p %i\n", N, r, p);
+ int ret = crypto_scrypt(reinterpret_cast<const uint8_t*>(Password.data()), Password.size(),
+ reinterpret_cast<const uint8_t*>(pwd->salt), pwd->salt_len,
+ N, r, p,
+ password_token, 32);
+ if (ret != 0) {
+ printf("scrypt error\n");
+ return false;
+ }
+ return true;
+}
+
+// Data structure for the *.weaver file, see Get_Weaver_Data below
+struct weaver_data_struct {
+ unsigned char version;
+ int slot;
+};
+
+/* C++ replacement for
+ * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#501
+ * called here
+ * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#768 */
+bool Get_Weaver_Data(const std::string& spblob_path, const std::string& handle_str, weaver_data_struct *wd) {
+ std::string weaver_file = spblob_path + handle_str + ".weaver";
+ std::string weaver_data;
+ if (!android::base::ReadFileToString(weaver_file, &weaver_data)) {
+ printf("Failed to read '%s'\n", weaver_file.c_str());
+ return false;
+ }
+ // output_hex(weaver_data.data(), weaver_data.size());printf("\n");
+ const unsigned char* byteptr = (const unsigned char*)weaver_data.data();
+ wd->version = *byteptr;
+ // printf("weaver version %i\n", wd->version);
+ const int* intptr = (const int*)weaver_data.data() + sizeof(unsigned char);
+ wd->slot = *intptr;
+ //endianswap(&wd->slot); not needed
+ // printf("weaver slot %i\n", wd->slot);
+ return true;
+}
+
+namespace android {
+
+// On Android 8.0 for some reason init can't seem to completely stop keystore
+// so we have to kill it too if it doesn't die on its own.
+static void kill_keystore() {
+ DIR* dir = opendir("/proc");
+ if (dir) {
+ struct dirent* de = 0;
+
+ while ((de = readdir(dir)) != 0) {
+ if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
+ continue;
+
+ int pid = -1;
+ int ret = sscanf(de->d_name, "%d", &pid);
+
+ if (ret == 1) {
+ char cmdpath[PATH_MAX];
+ sprintf(cmdpath, "/proc/%d/cmdline", pid);
+
+ FILE* file = fopen(cmdpath, "r");
+ size_t task_size = PATH_MAX;
+ char task[PATH_MAX];
+ char* p = task;
+ if (getline(&p, &task_size, file) > 0) {
+ if (strstr(task, "keystore") != 0) {
+ printf("keystore pid %d found, sending kill.\n", pid);
+ kill(pid, SIGINT);
+ usleep(5000);
+ kill(pid, SIGKILL);
+ }
+ }
+ fclose(file);
+ }
+ }
+ closedir(dir);
+ }
+}
+
+// The keystore holds a file open on /data so we have to stop / kill it
+// if we want to be able to unmount /data for things like formatting.
+static void stop_keystore() {
+ printf("Stopping keystore...\n");
+ property_set("ctl.stop", "keystore");
+ usleep(5000);
+ kill_keystore();
+}
+
+/* These next 2 functions try to get the keystore service 50 times because
+ * the keystore is not always ready when TWRP boots */
+android::sp<IBinder> getKeystoreBinder() {
+ android::sp<IServiceManager> sm = android::defaultServiceManager();
+ return sm->getService(String16("android.security.keystore"));
+}
+
+android::sp<IBinder> getKeystoreBinderRetry() {
+ printf("Starting keystore...\n");
+ property_set("ctl.start", "keystore");
+ int retry_count = 50;
+ android::sp<IBinder> binder = getKeystoreBinder();
+ while (binder == NULL && retry_count) {
+ printf("Waiting for keystore service... %i\n", retry_count--);
+ sleep(1);
+ binder = getKeystoreBinder();
+ }
+ return binder;
+}
+
+namespace keystore {
+
+#define SYNTHETIC_PASSWORD_VERSION_V1 1
+#define SYNTHETIC_PASSWORD_VERSION_V2 2
+#define SYNTHETIC_PASSWORD_VERSION_V3 3
+#define SYNTHETIC_PASSWORD_PASSWORD_BASED 0
+#define SYNTHETIC_PASSWORD_KEY_PREFIX "USRSKEY_synthetic_password_"
+#define USR_PRIVATE_KEY_PREFIX "USRPKEY_synthetic_password_"
+
+static std::string mKey_Prefix;
+
+/* The keystore alias subid is sometimes the same as the handle, but not always.
+ * In the case of handle 0c5303fd2010fe29, the alias subid used c5303fd2010fe29
+ * without the leading 0. We could try to parse the data from a previous
+ * keystore request, but I think this is an easier solution because there
+ * is little to no documentation on the format of data we get back from
+ * the keystore in this instance. We also want to copy everything to a temp
+ * folder so that any key upgrades that might take place do not actually
+ * upgrade the keys on the data partition. We rename all 1000 uid files to 0
+ * to pass the keystore permission checks. */
+bool Find_Keystore_Alias_SubID_And_Prep_Files(const userid_t user_id, std::string& keystoreid, const std::string& handle_str) {
+ char path_c[PATH_MAX];
+ sprintf(path_c, "/data/misc/keystore/user_%d", user_id);
+ char user_dir[PATH_MAX];
+ sprintf(user_dir, "user_%d", user_id);
+ std::string source_path = "/data/misc/keystore/";
+ source_path += user_dir;
+ std::string handle_sub = handle_str;
+ while (handle_sub.substr(0,1) == "0") {
+ std::string temp = handle_sub.substr(1);
+ handle_sub = temp;
+ }
+ mKey_Prefix = "";
+
+ mkdir("/tmp/misc", 0755);
+ mkdir("/tmp/misc/keystore", 0755);
+ std::string destination_path = "/tmp/misc/keystore/";
+ destination_path += user_dir;
+ if (mkdir(destination_path.c_str(), 0755) && errno != EEXIST) {
+ printf("failed to mkdir '%s' %s\n", destination_path.c_str(), strerror(errno));
+ return false;
+ }
+ destination_path += "/";
+
+ DIR* dir = opendir(source_path.c_str());
+ if (!dir) {
+ printf("Error opening '%s'\n", source_path.c_str());
+ return false;
+ }
+ source_path += "/";
+
+ struct dirent* de = 0;
+ size_t prefix_len = strlen(SYNTHETIC_PASSWORD_KEY_PREFIX);
+ bool found_subid = false;
+ bool has_pkey = false; // PKEY has priority over SKEY
+
+ while ((de = readdir(dir)) != 0) {
+ if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
+ continue;
+ if (!found_subid) {
+ size_t len = strlen(de->d_name);
+ if (len <= prefix_len)
+ continue;
+ if (strstr(de->d_name, SYNTHETIC_PASSWORD_KEY_PREFIX) && !has_pkey)
+ mKey_Prefix = SYNTHETIC_PASSWORD_KEY_PREFIX;
+ else if (strstr(de->d_name, USR_PRIVATE_KEY_PREFIX)) {
+ mKey_Prefix = USR_PRIVATE_KEY_PREFIX;
+ has_pkey = true;
+ } else
+ continue;
+ if (strstr(de->d_name, handle_sub.c_str())) {
+ keystoreid = handle_sub;
+ printf("keystoreid matched handle_sub: '%s'\n", keystoreid.c_str());
+ found_subid = true;
+ } else {
+ std::string file = de->d_name;
+ std::size_t found = file.find_last_of("_");
+ if (found != std::string::npos) {
+ keystoreid = file.substr(found + 1);
+ // printf("possible keystoreid: '%s'\n", keystoreid.c_str());
+ //found_subid = true; // we'll keep going in hopes that we find a pkey or a match to the handle_sub
+ }
+ }
+ }
+ std::string src = source_path;
+ src += de->d_name;
+ std::ifstream srcif(src.c_str(), std::ios::binary);
+ std::string dst = destination_path;
+ dst += de->d_name;
+ std::size_t source_uid = dst.find("1000");
+ if (source_uid != std::string::npos)
+ dst.replace(source_uid, 4, "0");
+ std::ofstream dstof(dst.c_str(), std::ios::binary);
+ printf("copying '%s' to '%s'\n", src.c_str(), dst.c_str());
+ dstof << srcif.rdbuf();
+ srcif.close();
+ dstof.close();
+ }
+ closedir(dir);
+ if (!found_subid && !mKey_Prefix.empty() && !keystoreid.empty())
+ found_subid = true;
+ return found_subid;
+}
+
+/* C++ replacement for function of the same name
+ * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#867
+ * returning an empty string indicates an error */
+std::string unwrapSyntheticPasswordBlob(const std::string& spblob_path, const std::string& handle_str, const userid_t user_id,
+ const void* application_id, const size_t application_id_size, uint32_t auth_token_len) {
+ std::string disk_decryption_secret_key = "";
+
+ android::ProcessState::self()->startThreadPool();
+
+ std::string keystore_alias_subid;
+ if (!Find_Keystore_Alias_SubID_And_Prep_Files(user_id, keystore_alias_subid, handle_str)) {
+ printf("failed to scan keystore alias subid and prep keystore files\n");
+ return disk_decryption_secret_key;
+ }
+
+ // First get the keystore service
+ android::sp<IBinder> binder = getKeystoreBinderRetry();
+ android::sp<IKeystoreService> service = interface_cast<IKeystoreService>(binder);
+
+ if (service == NULL) {
+ printf("error: could not connect to keystore service\n");
+ return disk_decryption_secret_key;
+ }
+
+ if (auth_token_len > 0) {
+ printf("Starting keystore_auth service...\n");
+ property_set("ctl.start", "keystore_auth");
+ }
+
+ // Read the data from the .spblob file per: https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#869
+ std::string spblob_file = spblob_path + handle_str + ".spblob";
+ std::string spblob_data;
+ if (!android::base::ReadFileToString(spblob_file, &spblob_data)) {
+ printf("Failed to read '%s'\n", spblob_file.c_str());
+ return disk_decryption_secret_key;
+ }
+ unsigned char* byteptr = (unsigned char*)spblob_data.data();
+ if (*byteptr != SYNTHETIC_PASSWORD_VERSION_V2 && *byteptr != SYNTHETIC_PASSWORD_VERSION_V1
+ && *byteptr != SYNTHETIC_PASSWORD_VERSION_V3) {
+ printf("Unsupported synthetic password version %i\n", *byteptr);
+ return disk_decryption_secret_key;
+ }
+ const unsigned char* synthetic_password_version = byteptr;
+ byteptr++;
+ if (*byteptr != SYNTHETIC_PASSWORD_PASSWORD_BASED) {
+ printf("spblob data is not SYNTHETIC_PASSWORD_PASSWORD_BASED\n");
+ return disk_decryption_secret_key;
+ }
+ byteptr++; // Now we're pointing to the blob data itself
+ if (*synthetic_password_version == SYNTHETIC_PASSWORD_VERSION_V2
+ || *synthetic_password_version == SYNTHETIC_PASSWORD_VERSION_V3) {
+ printf("spblob v2 / v3\n");
+ /* Version 2 / 3 of the spblob is basically the same as version 1, but the order of getting the intermediate key and disk decryption key have been flip-flopped
+ * as seen in https://android.googlesource.com/platform/frameworks/base/+/5025791ac6d1538224e19189397de8d71dcb1a12
+ */
+ /* First decrypt call found in
+ * https://android.googlesource.com/platform/frameworks/base/+/android-8.1.0_r18/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java#135
+ * We will use https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java
+ * and https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/keystore/java/android/security/keystore/AndroidKeyStoreAuthenticatedAESCipherSpi.java
+ * First we set some algorithm parameters as seen in two places:
+ * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/keystore/java/android/security/keystore/AndroidKeyStoreAuthenticatedAESCipherSpi.java#297
+ * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/keystore/java/android/security/keystore/AndroidKeyStoreAuthenticatedAESCipherSpi.java#216 */
+ // When using secdis (aka not weaver) you must supply an auth token to the keystore prior to the begin operation
+ if (auth_token_len > 0) {
+ /*::keystore::KeyStoreServiceReturnCode auth_result = service->addAuthToken(auth_token, auth_token_len);
+ if (!auth_result.isOk()) {
+ // The keystore checks the uid of the calling process and will return a permission denied on this operation for user 0
+ printf("keystore error adding auth token\n");
+ return disk_decryption_secret_key;
+ }*/
+ // The keystore refuses to allow the root user to supply auth tokens, so we write the auth token to a file earlier and
+ // run a separate service that runs user the system user to add the auth token. We wait for the auth token file to be
+ // deleted by the keymaster_auth service and check for a /auth_error file in case of errors. We quit after after a while if
+ // the /auth_token file never gets deleted.
+ int auth_wait_count = 20;
+ while (access("/auth_token", F_OK) == 0 && auth_wait_count-- > 0)
+ usleep(5000);
+ if (auth_wait_count == 0 || access("/auth_error", F_OK) == 0) {
+ printf("error during keymaster_auth service\n");
+ /* If you are getting this error, make sure that you have the keymaster_auth service defined in your init scripts, preferrably in init.recovery.{ro.hardware}.rc
+ * service keystore_auth /sbin/keystore_auth
+ * disabled
+ * oneshot
+ * user system
+ * group root
+ * seclabel u:r:recovery:s0
+ *
+ * And check dmesg for error codes regarding this service if needed. */
+ return disk_decryption_secret_key;
+ }
+ }
+ int32_t ret;
+ size_t maclen = 128;
+ unsigned char* iv = (unsigned char*)byteptr; // The IV is the first 12 bytes of the spblob
+ ::keystore::hidl_vec<uint8_t> iv_hidlvec;
+ iv_hidlvec.setToExternal((unsigned char*)byteptr, 12);
+ // printf("iv: "); output_hex((const unsigned char*)iv, 12); printf("\n");
+ std::string keystore_alias = mKey_Prefix;
+ keystore_alias += keystore_alias_subid;
+ String16 keystore_alias16(keystore_alias.data(), keystore_alias.size());
+ int32_t error_code;
+ unsigned char* cipher_text = (unsigned char*)byteptr + 12; // The cipher text comes immediately after the IV
+ std::string cipher_text_str(byteptr, byteptr + spblob_data.size() - 14);
+
+ ::keystore::hidl_vec<uint8_t> cipher_text_hidlvec;
+ ::keystore::AuthorizationSetBuilder begin_params;
+
+ cipher_text_hidlvec.setToExternal(cipher_text, spblob_data.size() - 14 /* 1 each for version and SYNTHETIC_PASSWORD_PASSWORD_BASED and 12 for the iv */);
+ begin_params.Authorization(::keystore::TAG_ALGORITHM, ::keystore::Algorithm::AES);
+ begin_params.Authorization(::keystore::TAG_BLOCK_MODE, ::keystore::BlockMode::GCM);
+ begin_params.Padding(::keystore::PaddingMode::NONE);
+ begin_params.Authorization(::keystore::TAG_NONCE, iv_hidlvec);
+ begin_params.Authorization(::keystore::TAG_MAC_LENGTH, maclen);
+
+ ::keystore::hidl_vec<uint8_t> entropy; // No entropy is needed for decrypt
+ entropy.resize(0);
+ android::security::keymaster::KeymasterArguments empty_params;
+ android::hardware::keymaster::V4_0::KeyPurpose decryptPurpose = android::hardware::keymaster::V4_0::KeyPurpose::DECRYPT;
+ android::sp<android::IBinder> decryptAuthToken(new android::BBinder);
+
+ android::sp<OperationResultPromise> promise = new OperationResultPromise;
+ auto future = promise->get_future();
+ auto binder_result = service->begin(promise, decryptAuthToken, keystore_alias16, (int32_t)decryptPurpose, true,
+ android::security::keymaster::KeymasterArguments(begin_params.hidl_data()),
+ entropy, -1, &error_code);
+ if (!binder_result.isOk()) {
+ printf("communication error while calling keystore\n");
+ return disk_decryption_secret_key;
+ }
+ ::keystore::KeyStoreNativeReturnCode rc(error_code);
+ if (!rc.isOk()) {
+ printf("Keystore begin returned: %u\n", error_code);
+ return disk_decryption_secret_key;
+ }
+ OperationResult result = future.get();
+ auto handle = std::move(result.token);
+
+ // The cipher.doFinal call triggers an update to the keystore followed by a finish https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java#64
+ // See also https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java#208
+ future = {};
+ promise = new OperationResultPromise();
+ future = promise->get_future();
+ binder_result = service->update(promise, handle, empty_params, cipher_text_hidlvec, &error_code);
+ rc = ::keystore::KeyStoreNativeReturnCode(error_code);
+ if (!rc.isOk()) {
+ printf("Keystore update returned: %d\n", error_code);
+ return disk_decryption_secret_key;
+ }
+ result = future.get();
+ if (!result.resultCode.isOk()) {
+ printf("update failed: %d\n", error_code);
+ return disk_decryption_secret_key;
+ }
+
+ size_t keystore_result_size = result.data.size();
+ unsigned char* keystore_result = (unsigned char*)malloc(keystore_result_size);
+ if (!keystore_result) {
+ printf("malloc on keystore_result\n");
+ return disk_decryption_secret_key;
+ }
+ memcpy(keystore_result, &result.data[0], result.data.size());
+ future = {};
+ promise = new OperationResultPromise();
+ future = promise->get_future();
+ ::keystore::hidl_vec<uint8_t> signature;
+ binder_result = service->finish(promise, handle, empty_params, signature, entropy, &error_code);
+ if (!binder_result.isOk()) {
+ printf("communication error while calling keystore\n");
+ free(keystore_result);
+ return disk_decryption_secret_key;
+ }
+ rc = ::keystore::KeyStoreNativeReturnCode(error_code);
+ if (!rc.isOk()) {
+ printf("Keystore finish returned: %d\n", error_code);
+ return disk_decryption_secret_key;
+ }
+ result = future.get();
+ if (!result.resultCode.isOk()) {
+ printf("finish failed: %d\n", error_code);
+ return disk_decryption_secret_key;
+ }
+ stop_keystore();
+ /* Now we do the second decrypt call as seen in:
+ * https://android.googlesource.com/platform/frameworks/base/+/android-8.1.0_r18/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java#136
+ */
+ const unsigned char* intermediate_iv = keystore_result;
+ // printf("intermediate_iv: "); output_hex((const unsigned char*)intermediate_iv, 12); printf("\n");
+ const unsigned char* intermediate_cipher_text = (const unsigned char*)keystore_result + 12; // The cipher text comes immediately after the IV
+ int cipher_size = keystore_result_size - 12;
+ // First we personalize as seen https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java#102
+ void* personalized_application_id = PersonalizedHashBinary(PERSONALISATION_APPLICATION_ID, (const char*)application_id, application_id_size);
+ if (!personalized_application_id) {
+ return disk_decryption_secret_key;
+ }
+ // printf("personalized application id: "); output_hex((unsigned char*)personalized_application_id, SHA512_DIGEST_LENGTH); printf("\n");
+ // Now we'll decrypt using openssl AES/GCM/NoPadding
+ OpenSSL_add_all_ciphers();
+ int actual_size=0, final_size=0;
+ EVP_CIPHER_CTX *d_ctx = EVP_CIPHER_CTX_new();
+ const unsigned char* key = (const unsigned char*)personalized_application_id; // The key is the now personalized copy of the application ID
+ // printf("key: "); output_hex((const unsigned char*)key, 32); printf("\n");
+ EVP_DecryptInit(d_ctx, EVP_aes_256_gcm(), key, intermediate_iv);
+ unsigned char* secret_key = (unsigned char*)malloc(cipher_size);
+ if (!secret_key) {
+ printf("malloc failure on secret key\n");
+ return disk_decryption_secret_key;
+ }
+ EVP_DecryptUpdate(d_ctx, secret_key, &actual_size, intermediate_cipher_text, cipher_size);
+ unsigned char tag[AES_BLOCK_SIZE];
+ EVP_CIPHER_CTX_ctrl(d_ctx, EVP_CTRL_GCM_SET_TAG, 16, tag);
+ EVP_DecryptFinal_ex(d_ctx, secret_key + actual_size, &final_size);
+ EVP_CIPHER_CTX_free(d_ctx);
+ free(personalized_application_id);
+ free(keystore_result);
+ int secret_key_real_size = actual_size - 16;
+ // printf("secret key: "); output_hex((const unsigned char*)secret_key, secret_key_real_size); printf("\n");
+ // The payload data from the keystore update is further personalized at https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#153
+ // We now have the disk decryption key!
+ if (*synthetic_password_version == SYNTHETIC_PASSWORD_VERSION_V3) {
+ // V3 uses SP800 instead of SHA512
+ disk_decryption_secret_key = PersonalizedHashSP800(PERSONALIZATION_FBE_KEY, PERSONALISATION_CONTEXT, (const char*)secret_key, secret_key_real_size);
+ } else {
+ disk_decryption_secret_key = PersonalizedHash(PERSONALIZATION_FBE_KEY, (const char*)secret_key, secret_key_real_size);
+ }
+ // printf("disk_decryption_secret_key: '%s'\n", disk_decryption_secret_key.c_str());
+ free(secret_key);
+ return disk_decryption_secret_key;
+ }
+ return disk_decryption_secret_key;
+}
+
+}}
+
+#define PASSWORD_TOKEN_SIZE 32
+
+/* C++ replacement for
+ * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#992
+ * called here
+ * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#813 */
+bool Get_Secdis(const std::string& spblob_path, const std::string& handle_str, std::string& secdis_data) {
+ std::string secdis_file = spblob_path + handle_str + ".secdis";
+ if (!android::base::ReadFileToString(secdis_file, &secdis_data)) {
+ printf("Failed to read '%s'\n", secdis_file.c_str());
+ return false;
+ }
+ // output_hex(secdis_data.data(), secdis_data.size());printf("\n");
+ return true;
+}
+
+// C++ replacement for https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#1033
+userid_t fakeUid(const userid_t uid) {
+ return 100000 + uid;
+}
+
+bool Is_Weaver(const std::string& spblob_path, const std::string& handle_str) {
+ std::string weaver_file = spblob_path + handle_str + ".weaver";
+ struct stat st;
+ if (stat(weaver_file.c_str(), &st) == 0)
+ return true;
+ return false;
+}
+
+bool Free_Return(bool retval, void* weaver_key, password_data_struct* pwd) {
+ if (weaver_key)
+ free(weaver_key);
+ if (pwd->salt)
+ free(pwd->salt);
+ if (pwd->password_handle)
+ free(pwd->password_handle);
+ return retval;
+}
+
+/* Decrypt_User_Synth_Pass is the TWRP C++ equivalent to spBasedDoVerifyCredential
+ * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/LockSettingsService.java#1998 */
+bool Decrypt_User_Synth_Pass(const userid_t user_id, const std::string& Password) {
+ bool retval = false;
+ void* weaver_key = NULL;
+ password_data_struct pwd;
+ pwd.salt = NULL;
+ pwd.salt_len = 0;
+ pwd.password_handle = NULL;
+ pwd.handle_len = 0;
+ char application_id[PASSWORD_TOKEN_SIZE + SHA512_DIGEST_LENGTH];
+
+ uint32_t auth_token_len = 0;
+
+ std::string secret; // this will be the disk decryption key that is sent to vold
+ std::string token = "!"; // there is no token used for this kind of decrypt, key escrow is handled by weaver
+ int flags = 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 (!fscrypt_unlock_user_key(user_id, 0, token.c_str(), secret.c_str())) {
+ printf("fscrypt_unlock_user_key returned fail\n");
+ return Free_Return(retval, weaver_key, &pwd);
+ }
+
+ if (!fscrypt_prepare_user_storage("", user_id, 0, flags)) {
+ printf("failed to fscrypt_prepare_user_storage\n");
+ return Free_Return(retval, weaver_key, &pwd);
+ }
+ printf("Decrypted Successfully!\n");
+ retval = true;
+ return Free_Return(retval, weaver_key, &pwd);
+}
+
+int Get_Password_Type(const userid_t user_id, std::string& filename) {
+ struct stat st;
+ char spblob_path_char[PATH_MAX];
+ sprintf(spblob_path_char, "/data/system_de/%d/spblob/", user_id);
+ if (stat(spblob_path_char, &st) == 0) {
+ printf("Using synthetic password method\n");
+ std::string spblob_path = spblob_path_char;
+ std::string handle_str;
+ if (!Find_Handle(spblob_path, handle_str)) {
+ printf("Error getting handle\n");
+ return 0;
+ }
+ printf("Handle is '%s'\n", handle_str.c_str());
+ password_data_struct pwd;
+ if (!Get_Password_Data(spblob_path, handle_str, &pwd)) {
+ printf("Failed to Get_Password_Data\n");
+ return 0;
+ }
+ if (pwd.password_type == 1) { // In Android this means pattern
+ printf("password type: pattern\n");
+ return 2; // In TWRP this means pattern
+ }
+ else if (pwd.password_type == 2) { // In Android this means PIN or password
+ printf("password type: pin\n");
+ return 1; // In TWRP this means PIN or password
+ }
+ printf("using default password\n");
+ return 0; // We'll try the default password
+ }
+ std::string path;
+ if (user_id == 0) {
+ path = "/data/system/";
+ } else {
+ char user_id_str[5];
+ sprintf(user_id_str, "%i", user_id);
+ path = "/data/system/users/";
+ path += user_id_str;
+ path += "/";
+ }
+ filename = path + "gatekeeper.password.key";
+ if (stat(filename.c_str(), &st) == 0 && st.st_size > 0)
+ return 1;
+ filename = path + "gatekeeper.pattern.key";
+ if (stat(filename.c_str(), &st) == 0 && st.st_size > 0)
+ return 2;
+ printf("Unable to locate gatekeeper password file '%s'\n", filename.c_str());
+ filename = "";
+ return 0;
+}
+
+bool Decrypt_User(const userid_t user_id, const std::string& Password) {
+ uint8_t *auth_token;
+ uint32_t auth_token_len;
+ int ret;
+
+ struct stat st;
+ if (user_id > 9999) {
+ printf("user_id is too big\n");
+ return false;
+ }
+ std::string filename;
+ bool Default_Password = (Password == "!");
+ if (Get_Password_Type(user_id, filename) == 0 && !Default_Password) {
+ printf("Unknown password type\n");
+ return false;
+ }
+ int flags = FLAG_STORAGE_DE;
+ if (user_id == 0)
+ flags = FLAG_STORAGE_DE;
+ else
+ flags = FLAG_STORAGE_CE;
+
+ if (Default_Password) {
+ if (!fscrypt_unlock_user_key(user_id, 0, "!", "!")) {
+ printf("unlock_user_key returned fail\n");
+ return false;
+ }
+ if (!fscrypt_prepare_user_storage("", user_id, 0, flags)) {
+ printf("failed to fscrypt_prepare_user_storage\n");
+ return false;
+ }
+ printf("Decrypted Successfully!\n");
+ return true;
+ }
+ if (stat("/data/system_de/0/spblob", &st) == 0) {
+ printf("Using synthetic password method\n");
+ return Decrypt_User_Synth_Pass(user_id, Password);
+ }
+ // printf("password filename is '%s'\n", filename.c_str());
+ if (stat(filename.c_str(), &st) != 0) {
+ printf("error stat'ing key file: %s\n", strerror(errno));
+ return false;
+ }
+ std::string handle;
+ if (!android::base::ReadFileToString(filename, &handle)) {
+ printf("Failed to read '%s'\n", filename.c_str());
+ return false;
+ }
+ bool should_reenroll;
+ bool request_reenroll = false;
+ android::sp<android::hardware::gatekeeper::V1_0::IGatekeeper> gk_device;
+ gk_device = ::android::hardware::gatekeeper::V1_0::IGatekeeper::getService();
+ if (gk_device == nullptr)
+ return false;
+ android::hardware::hidl_vec<uint8_t> curPwdHandle;
+ curPwdHandle.setToExternal(const_cast<uint8_t *>((const uint8_t *)handle.c_str()), st.st_size);
+ android::hardware::hidl_vec<uint8_t> enteredPwd;
+ enteredPwd.setToExternal(const_cast<uint8_t *>((const uint8_t *)Password.c_str()), Password.size());
+
+ android::hardware::Return<void> hwRet =
+ gk_device->verify(user_id, 0 /* challange */,
+ curPwdHandle,
+ enteredPwd,
+ [&ret, &request_reenroll, &auth_token, &auth_token_len]
+ (const android::hardware::gatekeeper::V1_0::GatekeeperResponse &rsp) {
+ ret = static_cast<int>(rsp.code); // propagate errors
+ if (rsp.code >= android::hardware::gatekeeper::V1_0::GatekeeperStatusCode::STATUS_OK) {
+ auth_token = new uint8_t[rsp.data.size()];
+ auth_token_len = rsp.data.size();
+ memcpy(auth_token, rsp.data.data(), auth_token_len);
+ request_reenroll = (rsp.code == android::hardware::gatekeeper::V1_0::GatekeeperStatusCode::STATUS_REENROLL);
+ ret = 0; // all success states are reported as 0
+ } else if (rsp.code == android::hardware::gatekeeper::V1_0::GatekeeperStatusCode::ERROR_RETRY_TIMEOUT && rsp.timeout > 0) {
+ ret = rsp.timeout;
+ }
+ }
+ );
+ if (!hwRet.isOk()) {
+ return false;
+ }
+
+ char token_hex[(auth_token_len*2)+1];
+ token_hex[(auth_token_len*2)] = 0;
+ uint32_t i;
+ for (i=0;i<auth_token_len;i++) {
+ sprintf(&token_hex[2*i], "%02X", auth_token[i]);
+ }
+ // The secret is "Android FBE credential hash" plus appended 0x00 to reach 128 bytes then append the user's password then feed that to sha512sum
+ std::string secret = HashPassword(Password);
+ if (!fscrypt_unlock_user_key(user_id, 0, token_hex, secret.c_str())) {
+ printf("fscrypt_unlock_user_key returned fail\n");
+ return false;
+ }
+
+ if (!fscrypt_prepare_user_storage("", user_id, 0, flags)) {
+ printf("failed to fscrypt_prepare_user_storage\n");
+ return false;
+ }
+ printf("Decrypted Successfully!\n");
+ return true;
+}
diff --git a/crypto/fscrypt/Decrypt.h b/crypto/fscrypt/Decrypt.h
new file mode 100755
index 0000000..8fb5160
--- /dev/null
+++ b/crypto/fscrypt/Decrypt.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdbool.h>
+#include <sys/cdefs.h>
+
+#include <cutils/multiuser.h>
+
+#include <string>
+
+__BEGIN_DECLS
+
+// NOTE: keep in sync with StorageManager
+static constexpr int FLAG_STORAGE_DE = 1 << 0;
+static constexpr int FLAG_STORAGE_CE = 1 << 1;
+// For 9.0 Ext4CryptPie.cpp
+static constexpr int STORAGE_FLAG_DE = 1 << 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/fscrypt/EncryptInplace.cpp b/crypto/fscrypt/EncryptInplace.cpp
new file mode 100644
index 0000000..3755718
--- /dev/null
+++ b/crypto/fscrypt/EncryptInplace.cpp
@@ -0,0 +1,623 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "EncryptInplace.h"
+
+#include <ext4_utils/ext4.h>
+#include <ext4_utils/ext4_utils.h>
+#include <f2fs_sparseblock.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <time.h>
+
+#include <algorithm>
+
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+
+// HORRIBLE HACK, FIXME
+#include "cryptfs.h"
+
+// FIXME horrible cut-and-paste code
+static inline int unix_read(int fd, void* buff, int len) {
+ return TEMP_FAILURE_RETRY(read(fd, buff, len));
+}
+
+static inline int unix_write(int fd, const void* buff, int len) {
+ return TEMP_FAILURE_RETRY(write(fd, buff, len));
+}
+
+#define CRYPT_SECTORS_PER_BUFSIZE (CRYPT_INPLACE_BUFSIZE / CRYPT_SECTOR_SIZE)
+
+/* aligned 32K writes tends to make flash happy.
+ * SD card association recommends it.
+ */
+#ifndef CONFIG_HW_DISK_ENCRYPTION
+#define BLOCKS_AT_A_TIME 8
+#else
+#define BLOCKS_AT_A_TIME 1024
+#endif
+
+struct encryptGroupsData {
+ int realfd;
+ int cryptofd;
+ off64_t numblocks;
+ off64_t one_pct, cur_pct, new_pct;
+ off64_t blocks_already_done, tot_numblocks;
+ off64_t used_blocks_already_done, tot_used_blocks;
+ const char* real_blkdev;
+ const char* crypto_blkdev;
+ int count;
+ off64_t offset;
+ char* buffer;
+ off64_t last_written_sector;
+ int completed;
+ time_t time_started;
+ int remaining_time;
+ bool set_progress_properties;
+};
+
+static void update_progress(struct encryptGroupsData* data, int is_used) {
+ data->blocks_already_done++;
+
+ if (is_used) {
+ data->used_blocks_already_done++;
+ }
+ if (data->tot_used_blocks) {
+ data->new_pct = data->used_blocks_already_done / data->one_pct;
+ } else {
+ data->new_pct = data->blocks_already_done / data->one_pct;
+ }
+
+ if (!data->set_progress_properties) return;
+
+ if (data->new_pct > data->cur_pct) {
+ char buf[8];
+ data->cur_pct = data->new_pct;
+ snprintf(buf, sizeof(buf), "%" PRId64, data->cur_pct);
+ android::base::SetProperty("vold.encrypt_progress", buf);
+ }
+
+ if (data->cur_pct >= 5) {
+ struct timespec time_now;
+ if (clock_gettime(CLOCK_MONOTONIC, &time_now)) {
+ LOG(WARNING) << "Error getting time";
+ } else {
+ double elapsed_time = difftime(time_now.tv_sec, data->time_started);
+ off64_t remaining_blocks = data->tot_used_blocks - data->used_blocks_already_done;
+ int remaining_time =
+ (int)(elapsed_time * remaining_blocks / data->used_blocks_already_done);
+
+ // Change time only if not yet set, lower, or a lot higher for
+ // best user experience
+ if (data->remaining_time == -1 || remaining_time < data->remaining_time ||
+ remaining_time > data->remaining_time + 60) {
+ char buf[8];
+ snprintf(buf, sizeof(buf), "%d", remaining_time);
+ android::base::SetProperty("vold.encrypt_time_remaining", buf);
+ data->remaining_time = remaining_time;
+ }
+ }
+ }
+}
+
+static void log_progress(struct encryptGroupsData const* data, bool completed) {
+ // Precondition - if completed data = 0 else data != 0
+
+ // Track progress so we can skip logging blocks
+ static off64_t offset = -1;
+
+ // Need to close existing 'Encrypting from' log?
+ if (completed || (offset != -1 && data->offset != offset)) {
+ LOG(INFO) << "Encrypted to sector " << offset / info.block_size * CRYPT_SECTOR_SIZE;
+ offset = -1;
+ }
+
+ // Need to start new 'Encrypting from' log?
+ if (!completed && offset != data->offset) {
+ LOG(INFO) << "Encrypting from sector " << data->offset / info.block_size * CRYPT_SECTOR_SIZE;
+ }
+
+ // Update offset
+ if (!completed) {
+ offset = data->offset + (off64_t)data->count * info.block_size;
+ }
+}
+
+static int flush_outstanding_data(struct encryptGroupsData* data) {
+ if (data->count == 0) {
+ return 0;
+ }
+
+ LOG(DEBUG) << "Copying " << data->count << " blocks at offset " << data->offset;
+
+ if (pread64(data->realfd, data->buffer, info.block_size * data->count, data->offset) <= 0) {
+ LOG(ERROR) << "Error reading real_blkdev " << data->real_blkdev << " for inplace encrypt";
+ return -1;
+ }
+
+ if (pwrite64(data->cryptofd, data->buffer, info.block_size * data->count, data->offset) <= 0) {
+ LOG(ERROR) << "Error writing crypto_blkdev " << data->crypto_blkdev
+ << " for inplace encrypt";
+ return -1;
+ } else {
+ log_progress(data, false);
+ }
+
+ data->count = 0;
+ data->last_written_sector =
+ (data->offset + data->count) / info.block_size * CRYPT_SECTOR_SIZE - 1;
+ return 0;
+}
+
+static int encrypt_groups(struct encryptGroupsData* data) {
+ unsigned int i;
+ u8* block_bitmap = 0;
+ unsigned int block;
+ off64_t ret;
+ int rc = -1;
+
+ data->buffer = (char*)malloc(info.block_size * BLOCKS_AT_A_TIME);
+ if (!data->buffer) {
+ LOG(ERROR) << "Failed to allocate crypto buffer";
+ goto errout;
+ }
+
+ block_bitmap = (u8*)malloc(info.block_size);
+ if (!block_bitmap) {
+ LOG(ERROR) << "failed to allocate block bitmap";
+ goto errout;
+ }
+
+ for (i = 0; i < aux_info.groups; ++i) {
+ LOG(INFO) << "Encrypting group " << i;
+
+ u32 first_block = aux_info.first_data_block + i * info.blocks_per_group;
+ u32 block_count = std::min(info.blocks_per_group, (u32)(aux_info.len_blocks - first_block));
+
+ off64_t offset = (u64)info.block_size * aux_info.bg_desc[i].bg_block_bitmap;
+
+ ret = pread64(data->realfd, block_bitmap, info.block_size, offset);
+ if (ret != (int)info.block_size) {
+ LOG(ERROR) << "failed to read all of block group bitmap " << i;
+ goto errout;
+ }
+
+ offset = (u64)info.block_size * first_block;
+
+ data->count = 0;
+
+ for (block = 0; block < block_count; block++) {
+ int used = (aux_info.bg_desc[i].bg_flags & EXT4_BG_BLOCK_UNINIT)
+ ? 0
+ : bitmap_get_bit(block_bitmap, block);
+ update_progress(data, used);
+ if (used) {
+ if (data->count == 0) {
+ data->offset = offset;
+ }
+ data->count++;
+ } else {
+ if (flush_outstanding_data(data)) {
+ goto errout;
+ }
+ }
+
+ offset += info.block_size;
+
+ /* Write data if we are aligned or buffer size reached */
+ if (offset % (info.block_size * BLOCKS_AT_A_TIME) == 0 ||
+ data->count == BLOCKS_AT_A_TIME) {
+ if (flush_outstanding_data(data)) {
+ goto errout;
+ }
+ }
+ }
+ if (flush_outstanding_data(data)) {
+ goto errout;
+ }
+ }
+
+ data->completed = 1;
+ rc = 0;
+
+errout:
+ log_progress(0, true);
+ free(data->buffer);
+ free(block_bitmap);
+ return rc;
+}
+
+static int cryptfs_enable_inplace_ext4(const char* crypto_blkdev, const char* real_blkdev,
+ off64_t size, off64_t* size_already_done, off64_t tot_size,
+ off64_t previously_encrypted_upto,
+ bool set_progress_properties) {
+ u32 i;
+ struct encryptGroupsData data;
+ int rc; // Can't initialize without causing warning -Wclobbered
+ int retries = RETRY_MOUNT_ATTEMPTS;
+ struct timespec time_started = {0};
+
+ if (previously_encrypted_upto > *size_already_done) {
+ LOG(DEBUG) << "Not fast encrypting since resuming part way through";
+ return -1;
+ }
+
+ memset(&data, 0, sizeof(data));
+ data.real_blkdev = real_blkdev;
+ data.crypto_blkdev = crypto_blkdev;
+ data.set_progress_properties = set_progress_properties;
+
+ LOG(DEBUG) << "Opening" << real_blkdev;
+ if ((data.realfd = open(real_blkdev, O_RDWR | O_CLOEXEC)) < 0) {
+ PLOG(ERROR) << "Error opening real_blkdev " << real_blkdev << " for inplace encrypt";
+ rc = -1;
+ goto errout;
+ }
+
+ LOG(DEBUG) << "Opening" << crypto_blkdev;
+ // Wait until the block device appears. Re-use the mount retry values since it is reasonable.
+ while ((data.cryptofd = open(crypto_blkdev, O_WRONLY | O_CLOEXEC)) < 0) {
+ if (--retries) {
+ PLOG(ERROR) << "Error opening crypto_blkdev " << crypto_blkdev
+ << " for ext4 inplace encrypt, retrying";
+ sleep(RETRY_MOUNT_DELAY_SECONDS);
+ } else {
+ PLOG(ERROR) << "Error opening crypto_blkdev " << crypto_blkdev
+ << " for ext4 inplace encrypt";
+ rc = ENABLE_INPLACE_ERR_DEV;
+ goto errout;
+ }
+ }
+
+ if (setjmp(setjmp_env)) { // NOLINT
+ LOG(ERROR) << "Reading ext4 extent caused an exception";
+ rc = -1;
+ goto errout;
+ }
+
+ if (read_ext(data.realfd, 0) != 0) {
+ LOG(ERROR) << "Failed to read ext4 extent";
+ rc = -1;
+ goto errout;
+ }
+
+ data.numblocks = size / CRYPT_SECTORS_PER_BUFSIZE;
+ data.tot_numblocks = tot_size / CRYPT_SECTORS_PER_BUFSIZE;
+ data.blocks_already_done = *size_already_done / CRYPT_SECTORS_PER_BUFSIZE;
+
+ LOG(INFO) << "Encrypting ext4 filesystem in place...";
+
+ data.tot_used_blocks = data.numblocks;
+ for (i = 0; i < aux_info.groups; ++i) {
+ data.tot_used_blocks -= aux_info.bg_desc[i].bg_free_blocks_count;
+ }
+
+ data.one_pct = data.tot_used_blocks / 100;
+ data.cur_pct = 0;
+
+ if (clock_gettime(CLOCK_MONOTONIC, &time_started)) {
+ LOG(WARNING) << "Error getting time at start";
+ // Note - continue anyway - we'll run with 0
+ }
+ data.time_started = time_started.tv_sec;
+ data.remaining_time = -1;
+
+ rc = encrypt_groups(&data);
+ if (rc) {
+ LOG(ERROR) << "Error encrypting groups";
+ goto errout;
+ }
+
+ *size_already_done += data.completed ? size : data.last_written_sector;
+ rc = 0;
+
+errout:
+ close(data.realfd);
+ close(data.cryptofd);
+
+ return rc;
+}
+
+static void log_progress_f2fs(u64 block, bool completed) {
+ // Precondition - if completed data = 0 else data != 0
+
+ // Track progress so we can skip logging blocks
+ static u64 last_block = (u64)-1;
+
+ // Need to close existing 'Encrypting from' log?
+ if (completed || (last_block != (u64)-1 && block != last_block + 1)) {
+ LOG(INFO) << "Encrypted to block " << last_block;
+ last_block = -1;
+ }
+
+ // Need to start new 'Encrypting from' log?
+ if (!completed && (last_block == (u64)-1 || block != last_block + 1)) {
+ LOG(INFO) << "Encrypting from block " << block;
+ }
+
+ // Update offset
+ if (!completed) {
+ last_block = block;
+ }
+}
+
+static int encrypt_one_block_f2fs(u64 pos, void* data) {
+ struct encryptGroupsData* priv_dat = (struct encryptGroupsData*)data;
+
+ priv_dat->blocks_already_done = pos - 1;
+ update_progress(priv_dat, 1);
+
+ off64_t offset = pos * CRYPT_INPLACE_BUFSIZE;
+
+ if (pread64(priv_dat->realfd, priv_dat->buffer, CRYPT_INPLACE_BUFSIZE, offset) <= 0) {
+ LOG(ERROR) << "Error reading real_blkdev " << priv_dat->crypto_blkdev
+ << " for f2fs inplace encrypt";
+ return -1;
+ }
+
+ if (pwrite64(priv_dat->cryptofd, priv_dat->buffer, CRYPT_INPLACE_BUFSIZE, offset) <= 0) {
+ LOG(ERROR) << "Error writing crypto_blkdev " << priv_dat->crypto_blkdev
+ << " for f2fs inplace encrypt";
+ return -1;
+ } else {
+ log_progress_f2fs(pos, false);
+ }
+
+ return 0;
+}
+
+static int cryptfs_enable_inplace_f2fs(const char* crypto_blkdev, const char* real_blkdev,
+ off64_t size, off64_t* size_already_done, off64_t tot_size,
+ off64_t previously_encrypted_upto,
+ bool set_progress_properties) {
+ struct encryptGroupsData data;
+ struct f2fs_info* f2fs_info = NULL;
+ int rc = ENABLE_INPLACE_ERR_OTHER;
+ if (previously_encrypted_upto > *size_already_done) {
+ LOG(DEBUG) << "Not fast encrypting since resuming part way through";
+ return ENABLE_INPLACE_ERR_OTHER;
+ }
+ memset(&data, 0, sizeof(data));
+ data.real_blkdev = real_blkdev;
+ data.crypto_blkdev = crypto_blkdev;
+ data.set_progress_properties = set_progress_properties;
+ data.realfd = -1;
+ data.cryptofd = -1;
+ if ((data.realfd = open64(real_blkdev, O_RDWR | O_CLOEXEC)) < 0) {
+ PLOG(ERROR) << "Error opening real_blkdev " << real_blkdev << " for f2fs inplace encrypt";
+ goto errout;
+ }
+ if ((data.cryptofd = open64(crypto_blkdev, O_WRONLY | O_CLOEXEC)) < 0) {
+ PLOG(ERROR) << "Error opening crypto_blkdev " << crypto_blkdev
+ << " for f2fs inplace encrypt";
+ rc = ENABLE_INPLACE_ERR_DEV;
+ goto errout;
+ }
+
+ f2fs_info = generate_f2fs_info(data.realfd);
+ if (!f2fs_info) goto errout;
+
+ data.numblocks = size / CRYPT_SECTORS_PER_BUFSIZE;
+ data.tot_numblocks = tot_size / CRYPT_SECTORS_PER_BUFSIZE;
+ data.blocks_already_done = *size_already_done / CRYPT_SECTORS_PER_BUFSIZE;
+
+ data.tot_used_blocks = get_num_blocks_used(f2fs_info);
+
+ data.one_pct = data.tot_used_blocks / 100;
+ data.cur_pct = 0;
+ data.time_started = time(NULL);
+ data.remaining_time = -1;
+
+ data.buffer = (char*)malloc(f2fs_info->block_size);
+ if (!data.buffer) {
+ LOG(ERROR) << "Failed to allocate crypto buffer";
+ goto errout;
+ }
+
+ data.count = 0;
+
+ /* Currently, this either runs to completion, or hits a nonrecoverable error */
+ rc = run_on_used_blocks(data.blocks_already_done, f2fs_info, &encrypt_one_block_f2fs, &data);
+
+ if (rc) {
+ LOG(ERROR) << "Error in running over f2fs blocks";
+ rc = ENABLE_INPLACE_ERR_OTHER;
+ goto errout;
+ }
+
+ *size_already_done += size;
+ rc = 0;
+
+errout:
+ if (rc) LOG(ERROR) << "Failed to encrypt f2fs filesystem on " << real_blkdev;
+
+ log_progress_f2fs(0, true);
+ free(f2fs_info);
+ free(data.buffer);
+ close(data.realfd);
+ close(data.cryptofd);
+
+ return rc;
+}
+
+static int cryptfs_enable_inplace_full(const char* crypto_blkdev, const char* real_blkdev,
+ off64_t size, off64_t* size_already_done, off64_t tot_size,
+ off64_t previously_encrypted_upto,
+ bool set_progress_properties) {
+ int realfd, cryptofd;
+ char* buf[CRYPT_INPLACE_BUFSIZE];
+ int rc = ENABLE_INPLACE_ERR_OTHER;
+ off64_t numblocks, i, remainder;
+ off64_t one_pct, cur_pct, new_pct;
+ off64_t blocks_already_done, tot_numblocks;
+
+ if ((realfd = open(real_blkdev, O_RDONLY | O_CLOEXEC)) < 0) {
+ PLOG(ERROR) << "Error opening real_blkdev " << real_blkdev << " for inplace encrypt";
+ return ENABLE_INPLACE_ERR_OTHER;
+ }
+
+ if ((cryptofd = open(crypto_blkdev, O_WRONLY | O_CLOEXEC)) < 0) {
+ PLOG(ERROR) << "Error opening crypto_blkdev " << crypto_blkdev << " for inplace encrypt";
+ close(realfd);
+ return ENABLE_INPLACE_ERR_DEV;
+ }
+
+ /* This is pretty much a simple loop of reading 4K, and writing 4K.
+ * The size passed in is the number of 512 byte sectors in the filesystem.
+ * So compute the number of whole 4K blocks we should read/write,
+ * and the remainder.
+ */
+ numblocks = size / CRYPT_SECTORS_PER_BUFSIZE;
+ remainder = size % CRYPT_SECTORS_PER_BUFSIZE;
+ tot_numblocks = tot_size / CRYPT_SECTORS_PER_BUFSIZE;
+ blocks_already_done = *size_already_done / CRYPT_SECTORS_PER_BUFSIZE;
+
+ LOG(ERROR) << "Encrypting filesystem in place...";
+
+ i = previously_encrypted_upto + 1 - *size_already_done;
+
+ if (lseek64(realfd, i * CRYPT_SECTOR_SIZE, SEEK_SET) < 0) {
+ PLOG(ERROR) << "Cannot seek to previously encrypted point on " << real_blkdev;
+ goto errout;
+ }
+
+ if (lseek64(cryptofd, i * CRYPT_SECTOR_SIZE, SEEK_SET) < 0) {
+ PLOG(ERROR) << "Cannot seek to previously encrypted point on " << crypto_blkdev;
+ goto errout;
+ }
+
+ for (; i < size && i % CRYPT_SECTORS_PER_BUFSIZE != 0; ++i) {
+ if (unix_read(realfd, buf, CRYPT_SECTOR_SIZE) <= 0) {
+ PLOG(ERROR) << "Error reading initial sectors from real_blkdev " << real_blkdev
+ << " for inplace encrypt";
+ goto errout;
+ }
+ if (unix_write(cryptofd, buf, CRYPT_SECTOR_SIZE) <= 0) {
+ PLOG(ERROR) << "Error writing initial sectors to crypto_blkdev " << crypto_blkdev
+ << " for inplace encrypt";
+ goto errout;
+ } else {
+ LOG(INFO) << "Encrypted 1 block at " << i;
+ }
+ }
+
+ one_pct = tot_numblocks / 100;
+ cur_pct = 0;
+ /* process the majority of the filesystem in blocks */
+ for (i /= CRYPT_SECTORS_PER_BUFSIZE; i < numblocks; i++) {
+ new_pct = (i + blocks_already_done) / one_pct;
+ if (set_progress_properties && new_pct > cur_pct) {
+ char property_buf[8];
+
+ cur_pct = new_pct;
+ snprintf(property_buf, sizeof(property_buf), "%" PRId64, cur_pct);
+ android::base::SetProperty("vold.encrypt_progress", property_buf);
+ }
+ if (unix_read(realfd, buf, CRYPT_INPLACE_BUFSIZE) <= 0) {
+ PLOG(ERROR) << "Error reading real_blkdev " << real_blkdev << " for inplace encrypt";
+ goto errout;
+ }
+ if (unix_write(cryptofd, buf, CRYPT_INPLACE_BUFSIZE) <= 0) {
+ PLOG(ERROR) << "Error writing crypto_blkdev " << crypto_blkdev << " for inplace encrypt";
+ goto errout;
+ } else {
+ LOG(DEBUG) << "Encrypted " << CRYPT_SECTORS_PER_BUFSIZE << " block at "
+ << i * CRYPT_SECTORS_PER_BUFSIZE;
+ }
+ }
+
+ /* Do any remaining sectors */
+ for (i = 0; i < remainder; i++) {
+ if (unix_read(realfd, buf, CRYPT_SECTOR_SIZE) <= 0) {
+ LOG(ERROR) << "Error reading final sectors from real_blkdev " << real_blkdev
+ << " for inplace encrypt";
+ goto errout;
+ }
+ if (unix_write(cryptofd, buf, CRYPT_SECTOR_SIZE) <= 0) {
+ LOG(ERROR) << "Error writing final sectors to crypto_blkdev " << crypto_blkdev
+ << " for inplace encrypt";
+ goto errout;
+ } else {
+ LOG(INFO) << "Encrypted 1 block at next location";
+ }
+ }
+
+ *size_already_done += size;
+ rc = 0;
+
+errout:
+ close(realfd);
+ close(cryptofd);
+
+ return rc;
+}
+
+/* returns on of the ENABLE_INPLACE_* return codes */
+int cryptfs_enable_inplace(const char* crypto_blkdev, const char* real_blkdev, off64_t size,
+ off64_t* size_already_done, off64_t tot_size,
+ off64_t previously_encrypted_upto, bool set_progress_properties) {
+ int rc_ext4, rc_f2fs, rc_full;
+ LOG(DEBUG) << "cryptfs_enable_inplace(" << crypto_blkdev << ", " << real_blkdev << ", " << size
+ << ", " << size_already_done << ", " << tot_size << ", " << previously_encrypted_upto
+ << ", " << set_progress_properties << ")";
+ if (previously_encrypted_upto) {
+ LOG(DEBUG) << "Continuing encryption from " << previously_encrypted_upto;
+ }
+
+ if (*size_already_done + size < previously_encrypted_upto) {
+ LOG(DEBUG) << "cryptfs_enable_inplace already done";
+ *size_already_done += size;
+ return 0;
+ }
+
+ /* TODO: identify filesystem type.
+ * As is, cryptfs_enable_inplace_ext4 will fail on an f2fs partition, and
+ * then we will drop down to cryptfs_enable_inplace_f2fs.
+ * */
+ if ((rc_ext4 = cryptfs_enable_inplace_ext4(crypto_blkdev, real_blkdev, size, size_already_done,
+ tot_size, previously_encrypted_upto,
+ set_progress_properties)) == 0) {
+ LOG(DEBUG) << "cryptfs_enable_inplace_ext4 success";
+ return 0;
+ }
+ LOG(DEBUG) << "cryptfs_enable_inplace_ext4()=" << rc_ext4;
+
+ if ((rc_f2fs = cryptfs_enable_inplace_f2fs(crypto_blkdev, real_blkdev, size, size_already_done,
+ tot_size, previously_encrypted_upto,
+ set_progress_properties)) == 0) {
+ LOG(DEBUG) << "cryptfs_enable_inplace_f2fs success";
+ return 0;
+ }
+ LOG(DEBUG) << "cryptfs_enable_inplace_f2fs()=" << rc_f2fs;
+
+ rc_full =
+ cryptfs_enable_inplace_full(crypto_blkdev, real_blkdev, size, size_already_done, tot_size,
+ previously_encrypted_upto, set_progress_properties);
+ LOG(DEBUG) << "cryptfs_enable_inplace_full()=" << rc_full;
+
+ /* Hack for b/17898962, the following is the symptom... */
+ if (rc_ext4 == ENABLE_INPLACE_ERR_DEV && rc_f2fs == ENABLE_INPLACE_ERR_DEV &&
+ rc_full == ENABLE_INPLACE_ERR_DEV) {
+ LOG(DEBUG) << "ENABLE_INPLACE_ERR_DEV";
+ return ENABLE_INPLACE_ERR_DEV;
+ }
+ return rc_full;
+}
diff --git a/crypto/fscrypt/EncryptInplace.h b/crypto/fscrypt/EncryptInplace.h
new file mode 100644
index 0000000..bf0c314
--- /dev/null
+++ b/crypto/fscrypt/EncryptInplace.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ENCRYPT_INPLACE_H
+#define _ENCRYPT_INPLACE_H
+
+#include <sys/types.h>
+
+#define CRYPT_INPLACE_BUFSIZE 4096
+#define CRYPT_SECTOR_SIZE 512
+#define RETRY_MOUNT_ATTEMPTS 10
+#define RETRY_MOUNT_DELAY_SECONDS 1
+
+int cryptfs_enable_inplace(const char* crypto_blkdev, const char* real_blkdev, off64_t size,
+ off64_t* size_already_done, off64_t tot_size,
+ off64_t previously_encrypted_upto, bool set_progress_properties);
+
+#endif
diff --git a/crypto/fscrypt/FsCrypt.cpp b/crypto/fscrypt/FsCrypt.cpp
new file mode 100755
index 0000000..f1c8809
--- /dev/null
+++ b/crypto/fscrypt/FsCrypt.cpp
@@ -0,0 +1,862 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "FsCrypt.h"
+
+#include "KeyStorage.h"
+#include "KeyUtil.h"
+#include "Utils.h"
+// #include "VoldUtil.h"
+
+#include <algorithm>
+#include <map>
+#include <set>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <selinux/android.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <private/android_filesystem_config.h>
+
+// #include "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 <fscrypt/fscrypt.h>
+#include <fs_mgr.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 <android-base/unique_fd.h>
+
+
+using android::base::StringPrintf;
+using android::fs_mgr::GetEntryForMountPoint;
+using android::vold::kEmptyAuthentication;
+using android::vold::KeyBuffer;
+// using android::vold::writeStringToFile;
+
+// Store main DE raw ref / policy
+std::string de_raw_ref;
+std::map<userid_t, std::string> s_de_key_raw_refs;
+std::map<userid_t, std::string> s_ce_key_raw_refs;
+
+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 + fscrypt_unencrypted_folder;
+const std::string device_key_path = device_key_dir + "/key";
+const std::string device_key_temp = device_key_dir + "/temp";
+
+const std::string user_key_dir = std::string() + DATA_MNT_POINT + "/misc/vold/user_keys";
+const std::string user_key_temp = user_key_dir + "/temp";
+const std::string prepare_subdirs_path = "/sbin/vold_prepare_subdirs";
+
+const std::string systemwide_volume_key_dir =
+ std::string() + DATA_MNT_POINT + "/misc/vold/volume_keys";
+const int STORAGE_FLAG_DE = 1;
+const int STORAGE_FLAG_CE = 2;
+
+bool s_systemwide_keys_initialized = false;
+
+android::fs_mgr::Fstab fstab_default;
+
+// Some users are ephemeral, don't try to wipe their keys from disk
+std::set<userid_t> s_ephemeral_users;
+
+// TODO abolish this map, per b/26948053
+std::map<userid_t, KeyBuffer> s_ce_keys;
+
+} // namespace
+
+static bool fscrypt_is_emulated() {
+ return property_get_bool("persist.sys.emulate_fbe", false);
+}
+
+static const char* escape_empty(const std::string& value) {
+ return value.empty() ? "null" : value.c_str();
+}
+
+static std::string get_de_key_path(userid_t user_id) {
+ return StringPrintf("%s/de/%d", user_key_dir.c_str(), user_id);
+}
+
+static std::string get_ce_key_directory_path(userid_t user_id) {
+ return StringPrintf("%s/ce/%d", user_key_dir.c_str(), user_id);
+}
+
+// Returns the keys newest first
+static std::vector<std::string> get_ce_key_paths(const std::string& directory_path) {
+ auto dirp = std::unique_ptr<DIR, int (*)(DIR*)>(opendir(directory_path.c_str()), closedir);
+ if (!dirp) {
+ PLOG(ERROR) << "Unable to open ce key directory: " + directory_path;
+ return std::vector<std::string>();
+ }
+ std::vector<std::string> result;
+ for (;;) {
+ errno = 0;
+ auto const entry = readdir(dirp.get());
+ if (!entry) {
+ if (errno) {
+ PLOG(ERROR) << "Unable to read ce key directory: " + directory_path;
+ return std::vector<std::string>();
+ }
+ break;
+ }
+ if (entry->d_type != DT_DIR || entry->d_name[0] != 'c') {
+ LOG(DEBUG) << "Skipping non-key " << entry->d_name;
+ continue;
+ }
+ result.emplace_back(directory_path + "/" + entry->d_name);
+ }
+ std::sort(result.begin(), result.end());
+ std::reverse(result.begin(), result.end());
+ return result;
+}
+
+static std::string get_ce_key_current_path(const std::string& directory_path) {
+ return directory_path + "/current";
+}
+
+static bool get_ce_key_new_path(const std::string& directory_path,
+ const std::vector<std::string>& paths, std::string* ce_key_path) {
+ if (paths.empty()) {
+ *ce_key_path = get_ce_key_current_path(directory_path);
+ return true;
+ }
+ for (unsigned int i = 0; i < UINT_MAX; i++) {
+ auto const candidate = StringPrintf("%s/cx%010u", directory_path.c_str(), i);
+ if (paths[0] < candidate) {
+ *ce_key_path = candidate;
+ return true;
+ }
+ }
+ return false;
+}
+
+// Discard all keys but the named one; rename it to canonical name.
+// No point in acting on errors in this; ignore them.
+static void fixate_user_ce_key(const std::string& directory_path, const std::string& to_fix,
+ const std::vector<std::string>& paths) {
+ for (auto const other_path : paths) {
+ if (other_path != to_fix) {
+ 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;
+ return;
+ }
+ }
+ android::vold::FsyncDirectory(directory_path);
+}
+
+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;
+ 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;
+ KeyBuffer ce_key;
+ if (!read_and_fixate_user_ce_key(user_id, auth, &ce_key)) return false;
+ std::string ce_raw_ref;
+ 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;
+ return true;
+}
+
+static bool prepare_dir(const std::string& dir, mode_t mode, uid_t uid, gid_t gid) {
+ LOG(DEBUG) << "Preparing: " << dir;
+ if (fs_prepare_dir(dir.c_str(), mode, uid, gid) != 0) {
+ PLOG(ERROR) << "Failed to prepare " << dir;
+ return false;
+ }
+ return true;
+}
+
+static bool destroy_dir(const std::string& dir) {
+ LOG(DEBUG) << "Destroying: " << dir;
+ if (rmdir(dir.c_str()) != 0 && errno != ENOENT) {
+ PLOG(ERROR) << "Failed to destroy " << dir;
+ return false;
+ }
+ return true;
+}
+
+// NB this assumes that there is only one thread listening for crypt commands, because
+// it creates keys in a fixed location.
+static bool create_and_install_user_keys(userid_t user_id, bool create_ephemeral) {
+ 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;
+ }
+ 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;
+ 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(DEBUG) << "Cannot find key for " << user_id;
+ return false;
+ }
+ *raw_ref = refi->second;
+ return true;
+}
+
+static void get_data_file_encryption_modes(PolicyKeyRef* key_ref) {
+ if (!ReadDefaultFstab(&fstab_default)) {
+ PLOG(ERROR) << "Failed to open default fstab";
+ return;
+ }
+ auto entry = android::fs_mgr::GetEntryForMountPoint(&fstab_default, DATA_MNT_POINT);
+ if (entry == nullptr) {
+ LOG(ERROR) << "get_data_file_encryption_modes::failed\n";
+ return;
+ }
+ key_ref->contents_mode = entry->file_contents_mode;
+ key_ref->filenames_mode = entry->file_names_mode;
+}
+
+static bool ensure_policy(const PolicyKeyRef& key_ref, const std::string& path) {
+ return fscrypt_policy_ensure(path.c_str(), key_ref.key_raw_ref.data(),
+ key_ref.key_raw_ref.size(), key_ref.contents_mode.c_str(),
+ key_ref.filenames_mode.c_str()) == 0;
+}
+
+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 = 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 (!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;
+ }
+ }
+ // fscrypt:TODO: go through all DE directories, ensure that all user dirs have the
+ // correct policy set on them, and that no rogue ones exist.
+ return true;
+}
+
+bool fscrypt_initialize_systemwide_keys() {
+ LOG(INFO) << "fscrypt_initialize_systemwide_keys";
+
+ if (s_systemwide_keys_initialized) {
+ LOG(INFO) << "Already initialized";
+ return true;
+ }
+
+ PolicyKeyRef device_ref;
+ if (!android::vold::retrieveAndInstallKey(true, kEmptyAuthentication, device_key_path,
+ device_key_temp, &device_ref.key_raw_ref))
+ 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") + fscrypt_key_mode;
+ if (!android::vold::writeStringToFile(modestring, mode_filename)) return false;
+
+ std::string ref_filename = std::string("/data") + fscrypt_key_ref;
+ if (!android::vold::writeStringToFile(device_ref.key_raw_ref, ref_filename)) return false;
+ LOG(INFO) << "Wrote system DE key reference to:" << ref_filename;
+
+ KeyBuffer per_boot_key;
+ if (!android::vold::randomKey(&per_boot_key)) return false;
+ std::string per_boot_raw_ref;
+ if (!android::vold::installKey(per_boot_key, &per_boot_raw_ref)) return false;
+ std::string per_boot_ref_filename = std::string("/data") + fscrypt_key_per_boot_ref;
+ if (!android::vold::writeStringToFile(per_boot_raw_ref, per_boot_ref_filename)) return false;
+ LOG(INFO) << "Wrote per boot key reference to:" << per_boot_ref_filename;
+
+ if (!android::vold::FsyncDirectory(device_key_dir)) return false;
+ s_systemwide_keys_initialized = true;
+ de_raw_ref = device_ref.key_raw_ref;
+ return true;
+}
+
+bool fscrypt_init_user0() {
+ if (!ReadDefaultFstab(&fstab_default)) {
+ PLOG(ERROR) << "Failed to open default fstab";
+ return -1;
+ }
+ if (fscrypt_is_native()) {
+ if (!prepare_dir(user_key_dir, 0700, AID_ROOT, AID_ROOT)) return false;
+ if (!prepare_dir(user_key_dir + "/ce", 0700, AID_ROOT, AID_ROOT)) return false;
+ if (!prepare_dir(user_key_dir + "/de", 0700, AID_ROOT, AID_ROOT)) return false;
+ if (!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;
+ }
+ // We can only safely prepare DE storage here, since CE keys are probably
+ // entangled with user credentials. The framework will always prepare CE
+ // storage once CE keys are installed.
+ if (!fscrypt_prepare_user_storage("", 0, 0, STORAGE_FLAG_DE)) {
+ LOG(ERROR) << "Failed to prepare user 0 storage";
+ return false;
+ }
+ // If this is a non-FBE device that recently left an emulated mode,
+ // restore user data directories to known-good state.
+ if (!fscrypt_is_native() && !fscrypt_is_emulated()) {
+ fscrypt_unlock_user_key(0, 0, "!", "!");
+ }
+
+ return true;
+}
+
+bool fscrypt_vold_create_user_key(userid_t user_id, int serial, bool ephemeral) {
+ LOG(DEBUG) << "fscrypt_vold_create_user_key for " << user_id << " serial " << serial;
+ if (!fscrypt_is_native()) {
+ return true;
+ }
+ // FIXME test for existence of key that is not loaded yet
+ if (s_ce_key_raw_refs.count(user_id) != 0) {
+ LOG(ERROR) << "Already exists, can't fscrypt_vold_create_user_key for " << user_id
+ << " serial " << serial;
+ // FIXME should we fail the command?
+ return true;
+ }
+ if (!create_and_install_user_keys(user_id, ephemeral)) {
+ return false;
+ }
+ return true;
+}
+
+static void drop_caches() {
+ // Clean any dirty pages (otherwise they won't be dropped).
+ sync();
+ // Drop inode and page caches.
+ if (!android::vold::writeStringToFile("3", "/proc/sys/vm/drop_caches")) {
+ PLOG(ERROR) << "Failed to drop caches during key eviction";
+ }
+}
+
+static bool evict_ce_key(userid_t user_id) {
+ s_ce_keys.erase(user_id);
+ bool success = true;
+ std::string raw_ref;
+ // If we haven't loaded the CE key, no need to evict it.
+ if (lookup_key_ref(s_ce_key_raw_refs, user_id, &raw_ref)) {
+ success &= android::vold::evictKey(raw_ref);
+ drop_caches();
+ }
+ s_ce_key_raw_refs.erase(user_id);
+ return success;
+}
+
+bool fscrypt_destroy_user_key(userid_t user_id) {
+ LOG(DEBUG) << "fscrypt_destroy_user_key(" << user_id << ")";
+ if (!fscrypt_is_native()) {
+ return true;
+ }
+ bool success = true;
+ std::string raw_ref;
+ success &= evict_ce_key(user_id);
+ success &=
+ lookup_key_ref(s_de_key_raw_refs, user_id, &raw_ref) && android::vold::evictKey(raw_ref);
+ s_de_key_raw_refs.erase(user_id);
+ auto it = s_ephemeral_users.find(user_id);
+ if (it != s_ephemeral_users.end()) {
+ s_ephemeral_users.erase(it);
+ } else {
+ for (auto const path : get_ce_key_paths(get_ce_key_directory_path(user_id))) {
+ success &= android::vold::destroyKey(path);
+ }
+ auto de_key_path = get_de_key_path(user_id);
+ if (android::vold::pathExists(de_key_path)) {
+ success &= android::vold::destroyKey(de_key_path);
+ } else {
+ LOG(INFO) << "Not present so not erasing: " << de_key_path;
+ }
+ }
+ return success;
+}
+
+static bool emulated_lock(const std::string& path) {
+ if (chmod(path.c_str(), 0000) != 0) {
+ PLOG(ERROR) << "Failed to chmod " << path;
+ return false;
+ }
+#if EMULATED_USES_SELINUX
+ if (setfilecon(path.c_str(), "u:object_r:storage_stub_file:s0") != 0) {
+ PLOG(WARNING) << "Failed to setfilecon " << path;
+ return false;
+ }
+#endif
+ return true;
+}
+
+static bool emulated_unlock(const std::string& path, mode_t mode) {
+ if (chmod(path.c_str(), mode) != 0) {
+ PLOG(ERROR) << "Failed to chmod " << path;
+ // FIXME temporary workaround for b/26713622
+ if (fscrypt_is_emulated()) return false;
+ }
+#if EMULATED_USES_SELINUX
+ if (selinux_android_restorecon(path.c_str(), SELINUX_ANDROID_RESTORECON_FORCE) != 0) {
+ PLOG(WARNING) << "Failed to restorecon " << path;
+ // FIXME temporary workaround for b/26713622
+ if (fscrypt_is_emulated()) return false;
+ }
+#endif
+ return true;
+}
+
+static bool parse_hex(const std::string& hex, std::string* result) {
+ if (hex == "!") {
+ *result = "";
+ return true;
+ }
+ if (android::vold::HexToStr(hex, *result) != 0) {
+ LOG(ERROR) << "Invalid FBE hex string"; // Don't log the string for security reasons
+ return false;
+ }
+ return true;
+}
+
+static std::string volkey_path(const std::string& misc_path, const std::string& volume_uuid) {
+ return misc_path + "/vold/volume_keys/" + volume_uuid + "/default";
+}
+
+static std::string volume_secdiscardable_path(const std::string& volume_uuid) {
+ return systemwide_volume_key_dir + "/" + volume_uuid + "/secdiscardable";
+}
+
+static bool read_or_create_volkey(const std::string& misc_path, const std::string& volume_uuid,
+ PolicyKeyRef* key_ref) {
+ auto secdiscardable_path = volume_secdiscardable_path(volume_uuid);
+ std::string secdiscardable_hash;
+ if (android::vold::pathExists(secdiscardable_path)) {
+ if (!android::vold::readSecdiscardable(secdiscardable_path, &secdiscardable_hash))
+ return false;
+ } else {
+ if (fs_mkdirs(secdiscardable_path.c_str(), 0700) != 0) {
+ PLOG(ERROR) << "Creating directories for: " << secdiscardable_path;
+ return false;
+ }
+ if (!android::vold::createSecdiscardable(secdiscardable_path, &secdiscardable_hash))
+ return false;
+ }
+ auto key_path = volkey_path(misc_path, volume_uuid);
+ if (fs_mkdirs(key_path.c_str(), 0700) != 0) {
+ PLOG(ERROR) << "Creating directories for: " << key_path;
+ return false;
+ }
+ android::vold::KeyAuthentication auth("", secdiscardable_hash);
+ if (!android::vold::retrieveAndInstallKey(true, auth, key_path, key_path + "_tmp",
+ &key_ref->key_raw_ref))
+ return false;
+ key_ref->contents_mode =
+ android::base::GetProperty("ro.crypto.volume.contents_mode", "aes-256-xts");
+ key_ref->filenames_mode =
+ android::base::GetProperty("ro.crypto.volume.filenames_mode", "aes-256-heh");
+ return true;
+}
+
+static bool destroy_volkey(const std::string& misc_path, const std::string& volume_uuid) {
+ auto path = volkey_path(misc_path, volume_uuid);
+ if (!android::vold::pathExists(path)) return true;
+ return android::vold::destroyKey(path);
+}
+
+bool fscrypt_add_user_key_auth(userid_t user_id, int serial, const std::string& token_hex,
+ const std::string& secret_hex) {
+ LOG(DEBUG) << "fscrypt_add_user_key_auth " << user_id << " serial=" << serial
+ << " token_present=" << (token_hex != "!");
+ if (!fscrypt_is_native()) return true;
+ if (s_ephemeral_users.count(user_id) != 0) return true;
+ std::string token, secret;
+ if (!parse_hex(token_hex, &token)) return false;
+ if (!parse_hex(secret_hex, &secret)) return false;
+ auto auth =
+ secret.empty() ? kEmptyAuthentication : android::vold::KeyAuthentication(token, secret);
+ auto it = s_ce_keys.find(user_id);
+ if (it == s_ce_keys.end()) {
+ LOG(ERROR) << "Key not loaded into memory, can't change for user " << user_id;
+ return false;
+ }
+ const auto& ce_key = it->second;
+ auto const directory_path = get_ce_key_directory_path(user_id);
+ 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, auth, ce_key)) return false;
+ if (!android::vold::FsyncDirectory(directory_path)) return false;
+ return true;
+}
+
+bool fscrypt_fixate_newest_user_key_auth(userid_t user_id) {
+ LOG(DEBUG) << "fscrypt_fixate_newest_user_key_auth " << user_id;
+ if (!fscrypt_is_native()) return true;
+ if (s_ephemeral_users.count(user_id) != 0) return true;
+ auto const directory_path = get_ce_key_directory_path(user_id);
+ auto const paths = get_ce_key_paths(directory_path);
+ if (paths.empty()) {
+ LOG(ERROR) << "No ce keys present, cannot fixate for user " << user_id;
+ return false;
+ }
+ fixate_user_ce_key(directory_path, paths[0], paths);
+ return true;
+}
+
+// TODO: rename to 'install' for consistency, and take flags to know which keys to install
+bool fscrypt_unlock_user_key(userid_t user_id, int serial, const std::string& token_hex,
+ const std::string& secret_hex) {
+ LOG(DEBUG) << "fscrypt_unlock_user_key " << user_id << " serial=" << serial
+ << " token_present=" << (token_hex != "!");
+ if (fscrypt_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 {
+ // 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;
+ return false;
+ }
+ }
+ return true;
+}
+
+// TODO: rename to 'evict' for consistency
+bool fscrypt_lock_user_key(userid_t user_id) {
+ LOG(DEBUG) << "fscrypt_lock_user_key " << user_id;
+ if (fscrypt_is_native()) {
+ return evict_ce_key(user_id);
+ } else if (fscrypt_is_emulated()) {
+ // When in emulation mode, we just use chmod
+ if (!emulated_lock(android::vold::BuildDataSystemCePath(user_id)) ||
+ !emulated_lock(android::vold::BuildDataMiscCePath(user_id)) ||
+ !emulated_lock(android::vold::BuildDataMediaCePath("", user_id)) ||
+ !emulated_lock(android::vold::BuildDataUserCePath("", user_id))) {
+ LOG(ERROR) << "Failed to lock user " << user_id;
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static bool prepare_subdirs(const std::string& action, const std::string& volume_uuid,
+ userid_t user_id, int flags) {
+ if (0 != android::vold::ForkExecvp(
+ std::vector<std::string>{prepare_subdirs_path, action, volume_uuid,
+ std::to_string(user_id), std::to_string(flags)})) {
+ LOG(ERROR) << "vold_prepare_subdirs failed";
+ return false;
+ }
+ return true;
+}
+
+bool fscrypt_prepare_user_storage(const std::string& volume_uuid, userid_t user_id, int serial,
+ int flags) {
+ LOG(DEBUG) << "fscrypt_prepare_user_storage for volume " << escape_empty(volume_uuid)
+ << ", user " << user_id << ", serial " << serial << ", flags " << flags;
+
+ if (flags & STORAGE_FLAG_DE) {
+ // DE_sys key
+ auto system_legacy_path = android::vold::BuildDataSystemLegacyPath(user_id);
+ auto misc_legacy_path = android::vold::BuildDataMiscLegacyPath(user_id);
+ auto profiles_de_path = android::vold::BuildDataProfilesDePath(user_id);
+
+ // DE_n key
+ auto system_de_path = android::vold::BuildDataSystemDePath(user_id);
+ auto misc_de_path = android::vold::BuildDataMiscDePath(user_id);
+ auto vendor_de_path = android::vold::BuildDataVendorDePath(user_id);
+ auto user_de_path = android::vold::BuildDataUserDePath(volume_uuid, user_id);
+
+ if (volume_uuid.empty()) {
+ if (!prepare_dir(system_legacy_path, 0700, AID_SYSTEM, AID_SYSTEM)) return false;
+#if MANAGE_MISC_DIRS
+ if (!prepare_dir(misc_legacy_path, 0750, multiuser_get_uid(user_id, AID_SYSTEM),
+ multiuser_get_uid(user_id, AID_EVERYBODY)))
+ return false;
+#endif
+ if (!prepare_dir(profiles_de_path, 0771, AID_SYSTEM, AID_SYSTEM)) return false;
+
+ if (!prepare_dir(system_de_path, 0770, AID_SYSTEM, AID_SYSTEM)) return false;
+ if (!prepare_dir(misc_de_path, 01771, AID_SYSTEM, AID_MISC)) return false;
+ if (!prepare_dir(vendor_de_path, 0771, AID_ROOT, AID_ROOT)) return false;
+ }
+ if (!prepare_dir(user_de_path, 0771, AID_SYSTEM, AID_SYSTEM)) return false;
+
+ if (fscrypt_is_native()) {
+ PolicyKeyRef de_ref;
+ if (volume_uuid.empty()) {
+ if (!lookup_key_ref(s_de_key_raw_refs, user_id, &de_ref.key_raw_ref)) return false;
+ get_data_file_encryption_modes(&de_ref);
+ if (!ensure_policy(de_ref, system_de_path)) return false;
+ if (!ensure_policy(de_ref, misc_de_path)) return false;
+ if (!ensure_policy(de_ref, vendor_de_path)) return false;
+ } else {
+ if (!read_or_create_volkey(misc_de_path, volume_uuid, &de_ref)) return false;
+ }
+ if (!ensure_policy(de_ref, user_de_path)) return false;
+ }
+ }
+ if (flags & STORAGE_FLAG_CE) {
+ // CE_n key
+ auto system_ce_path = android::vold::BuildDataSystemCePath(user_id);
+ auto misc_ce_path = android::vold::BuildDataMiscCePath(user_id);
+ auto vendor_ce_path = android::vold::BuildDataVendorCePath(user_id);
+ auto media_ce_path = android::vold::BuildDataMediaCePath(volume_uuid, user_id);
+ auto user_ce_path = android::vold::BuildDataUserCePath(volume_uuid, user_id);
+
+ if (volume_uuid.empty()) {
+ if (!prepare_dir(system_ce_path, 0770, AID_SYSTEM, AID_SYSTEM)) return false;
+ if (!prepare_dir(misc_ce_path, 01771, AID_SYSTEM, AID_MISC)) return false;
+ if (!prepare_dir(vendor_ce_path, 0771, AID_ROOT, AID_ROOT)) return false;
+ }
+ if (!prepare_dir(media_ce_path, 0770, AID_MEDIA_RW, AID_MEDIA_RW)) return false;
+ if (!prepare_dir(user_ce_path, 0771, AID_SYSTEM, AID_SYSTEM)) return false;
+
+ if (fscrypt_is_native()) {
+ PolicyKeyRef ce_ref;
+ if (volume_uuid.empty()) {
+ if (!lookup_key_ref(s_ce_key_raw_refs, user_id, &ce_ref.key_raw_ref)) return false;
+ get_data_file_encryption_modes(&ce_ref);
+ if (!ensure_policy(ce_ref, system_ce_path)) return false;
+ if (!ensure_policy(ce_ref, misc_ce_path)) return false;
+ if (!ensure_policy(ce_ref, vendor_ce_path)) return false;
+
+ } else {
+ if (!read_or_create_volkey(misc_ce_path, volume_uuid, &ce_ref)) return false;
+ }
+ if (!ensure_policy(ce_ref, media_ce_path)) return false;
+ if (!ensure_policy(ce_ref, user_ce_path)) return false;
+ }
+
+ if (volume_uuid.empty()) {
+ // Now that credentials have been installed, we can run restorecon
+ // over these paths
+ // NOTE: these paths need to be kept in sync with libselinux
+ android::vold::RestoreconRecursive(system_ce_path);
+ android::vold::RestoreconRecursive(vendor_ce_path);
+ android::vold::RestoreconRecursive(misc_ce_path);
+ }
+ }
+ if (!prepare_subdirs("prepare", volume_uuid, user_id, flags)) return false;
+
+ return true;
+}
+
+bool fscrypt_destroy_user_storage(const std::string& volume_uuid, userid_t user_id, int flags) {
+ LOG(DEBUG) << "fscrypt_destroy_user_storage for volume " << escape_empty(volume_uuid)
+ << ", user " << user_id << ", flags " << flags;
+ bool res = true;
+
+ res &= prepare_subdirs("destroy", volume_uuid, user_id, flags);
+
+ if (flags & STORAGE_FLAG_CE) {
+ // CE_n key
+ auto system_ce_path = android::vold::BuildDataSystemCePath(user_id);
+ auto misc_ce_path = android::vold::BuildDataMiscCePath(user_id);
+ auto vendor_ce_path = android::vold::BuildDataVendorCePath(user_id);
+ auto media_ce_path = android::vold::BuildDataMediaCePath(volume_uuid, user_id);
+ auto user_ce_path = android::vold::BuildDataUserCePath(volume_uuid, user_id);
+
+ res &= destroy_dir(media_ce_path);
+ res &= destroy_dir(user_ce_path);
+ if (volume_uuid.empty()) {
+ res &= destroy_dir(system_ce_path);
+ res &= destroy_dir(misc_ce_path);
+ res &= destroy_dir(vendor_ce_path);
+ } else {
+ if (fscrypt_is_native()) {
+ res &= destroy_volkey(misc_ce_path, volume_uuid);
+ }
+ }
+ }
+
+ if (flags & STORAGE_FLAG_DE) {
+ // DE_sys key
+ auto system_legacy_path = android::vold::BuildDataSystemLegacyPath(user_id);
+ auto misc_legacy_path = android::vold::BuildDataMiscLegacyPath(user_id);
+ auto profiles_de_path = android::vold::BuildDataProfilesDePath(user_id);
+
+ // DE_n key
+ auto system_de_path = android::vold::BuildDataSystemDePath(user_id);
+ auto misc_de_path = android::vold::BuildDataMiscDePath(user_id);
+ auto vendor_de_path = android::vold::BuildDataVendorDePath(user_id);
+ auto user_de_path = android::vold::BuildDataUserDePath(volume_uuid, user_id);
+
+ res &= destroy_dir(user_de_path);
+ if (volume_uuid.empty()) {
+ res &= destroy_dir(system_legacy_path);
+#if MANAGE_MISC_DIRS
+ res &= destroy_dir(misc_legacy_path);
+#endif
+ res &= destroy_dir(profiles_de_path);
+ res &= destroy_dir(system_de_path);
+ res &= destroy_dir(misc_de_path);
+ res &= destroy_dir(vendor_de_path);
+ } else {
+ if (fscrypt_is_native()) {
+ res &= destroy_volkey(misc_de_path, volume_uuid);
+ }
+ }
+ }
+
+ return res;
+}
+
+static bool destroy_volume_keys(const std::string& directory_path, const std::string& volume_uuid) {
+ auto dirp = std::unique_ptr<DIR, int (*)(DIR*)>(opendir(directory_path.c_str()), closedir);
+ if (!dirp) {
+ PLOG(ERROR) << "Unable to open directory: " + directory_path;
+ return false;
+ }
+ bool res = true;
+ for (;;) {
+ errno = 0;
+ auto const entry = readdir(dirp.get());
+ if (!entry) {
+ if (errno) {
+ PLOG(ERROR) << "Unable to read directory: " + directory_path;
+ return false;
+ }
+ break;
+ }
+ if (entry->d_type != DT_DIR || entry->d_name[0] == '.') {
+ LOG(DEBUG) << "Skipping non-user " << entry->d_name;
+ continue;
+ }
+ res &= destroy_volkey(directory_path + "/" + entry->d_name, volume_uuid);
+ }
+ return res;
+}
+
+bool fscrypt_destroy_volume_keys(const std::string& volume_uuid) {
+ bool res = true;
+ LOG(DEBUG) << "fscrypt_destroy_volume_keys for volume " << escape_empty(volume_uuid);
+ auto secdiscardable_path = volume_secdiscardable_path(volume_uuid);
+ res &= android::vold::runSecdiscardSingle(secdiscardable_path);
+ res &= destroy_volume_keys("/data/misc_ce", volume_uuid);
+ res &= destroy_volume_keys("/data/misc_de", volume_uuid);
+ return res;
+}
diff --git a/crypto/fscrypt/FsCrypt.h b/crypto/fscrypt/FsCrypt.h
new file mode 100755
index 0000000..ff32bc8
--- /dev/null
+++ b/crypto/fscrypt/FsCrypt.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string>
+#include <map>
+#include <vector>
+
+#include <cutils/multiuser.h>
+
+bool fscrypt_initialize_systemwide_keys();
+
+bool fscrypt_init_user0();
+bool fscrypt_vold_create_user_key(userid_t user_id, int serial, bool ephemeral);
+bool fscrypt_destroy_user_key(userid_t user_id);
+bool fscrypt_add_user_key_auth(userid_t user_id, int serial, const std::string& token,
+ const std::string& secret);
+bool fscrypt_fixate_newest_user_key_auth(userid_t user_id);
+
+bool fscrypt_unlock_user_key(userid_t user_id, int serial, const std::string& token,
+ const std::string& secret);
+bool fscrypt_lock_user_key(userid_t user_id);
+
+bool fscrypt_prepare_user_storage(const std::string& volume_uuid, userid_t user_id, int serial,
+ int flags);
+bool fscrypt_destroy_user_storage(const std::string& volume_uuid, userid_t user_id, int flags);
+
+bool fscrypt_destroy_volume_keys(const std::string& volume_uuid);
+
+bool lookup_key_ref(const std::map<userid_t, std::string>& key_map, userid_t user_id,
+ std::string* raw_ref);
\ No newline at end of file
diff --git a/crypto/fscrypt/HashPassword.cpp b/crypto/fscrypt/HashPassword.cpp
new file mode 100644
index 0000000..07ecb1f
--- /dev/null
+++ b/crypto/fscrypt/HashPassword.cpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2016 Team Win Recovery Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * This computes the "secret" used by Android as one of the parameters
+ * to decrypt File Based Encryption. The secret is prefixed with
+ * "Android FBE credential hash" padded with 0s to 128 bytes then the
+ * user's password is appended to the end of the 128 bytes. This string
+ * is then hashed with sha512 and the sha512 value is then converted to
+ * hex with upper-case characters.
+ */
+
+#include <stdio.h>
+#include <string>
+#include <stdlib.h>
+#include <openssl/sha.h>
+#include <openssl/hmac.h>
+
+#include "HashPassword.h"
+
+#define PASS_PADDING_SIZE 128
+#define SHA512_HEX_SIZE SHA512_DIGEST_LENGTH * 2
+#define SHA256_HEX_SIZE SHA256_DIGEST_LENGTH * 2
+
+void* PersonalizedHashBinary(const char* prefix, const char* key, const size_t key_size) {
+ size_t size = PASS_PADDING_SIZE + key_size;
+ unsigned char* buffer = (unsigned char*)calloc(1, size);
+ if (!buffer) return NULL; // failed to malloc
+ memcpy((void*)buffer, (void*)prefix, strlen(prefix));
+ unsigned char* ptr = buffer + PASS_PADDING_SIZE;
+ memcpy((void*)ptr, key, key_size);
+ unsigned char hash[SHA512_DIGEST_LENGTH];
+ SHA512_CTX sha512;
+ SHA512_Init(&sha512);
+ SHA512_Update(&sha512, buffer, size);
+ SHA512_Final(hash, &sha512);
+ free(buffer);
+ void* ret = malloc(SHA512_DIGEST_LENGTH);
+ if (!ret) return NULL; // failed to malloc
+ memcpy(ret, (void*)&hash[0], SHA512_DIGEST_LENGTH);
+ return ret;
+}
+
+std::string PersonalizedHash(const char* prefix, const char* key, const size_t key_size) {
+ size_t size = PASS_PADDING_SIZE + key_size;
+ unsigned char* buffer = (unsigned char*)calloc(1, size);
+ if (!buffer) return ""; // failed to malloc
+ memcpy((void*)buffer, (void*)prefix, strlen(prefix));
+ unsigned char* ptr = buffer + PASS_PADDING_SIZE;
+ memcpy((void*)ptr, key, key_size);
+ unsigned char hash[SHA512_DIGEST_LENGTH];
+ SHA512_CTX sha512;
+ SHA512_Init(&sha512);
+ SHA512_Update(&sha512, buffer, size);
+ SHA512_Final(hash, &sha512);
+ int index = 0;
+ char hex_hash[SHA512_HEX_SIZE + 1];
+ for(index = 0; index < SHA512_DIGEST_LENGTH; index++)
+ sprintf(hex_hash + (index * 2), "%02X", hash[index]);
+ hex_hash[128] = 0;
+ std::string ret = hex_hash;
+ free(buffer);
+ return ret;
+}
+
+std::string PersonalizedHash(const char* prefix, const std::string& Password) {
+ return PersonalizedHash(prefix, Password.c_str(), Password.size());
+}
+
+std::string PersonalizedHashSP800(const char* label, const char* context, const char* key, const size_t key_size) {
+ HMAC_CTX ctx;
+ HMAC_CTX_init(&ctx);
+ HMAC_Init_ex(&ctx, key, key_size, EVP_sha256(), NULL);
+ unsigned int counter = 1;
+ endianswap(&counter);
+ HMAC_Update(&ctx, (const unsigned char*)&counter, 4);
+ HMAC_Update(&ctx, (const unsigned char*)label, strlen(label));
+ const unsigned char divider = 0;
+ HMAC_Update(&ctx, ÷r, 1);
+ HMAC_Update(&ctx, (const unsigned char*)context, strlen(context));
+ unsigned int contextDisambiguation = strlen(context) * 8;
+ endianswap(&contextDisambiguation);
+ HMAC_Update(&ctx, (const unsigned char*)&contextDisambiguation, 4);
+ unsigned int finalValue = 256;
+ endianswap(&finalValue);
+ HMAC_Update(&ctx, (const unsigned char*)&finalValue, 4);
+
+ unsigned char output[SHA256_DIGEST_LENGTH];
+ unsigned int out_size = 0;
+ HMAC_Final(&ctx, output, &out_size);
+
+ int index = 0;
+ char hex_hash[SHA256_HEX_SIZE + 1];
+ for(index = 0; index < SHA256_DIGEST_LENGTH; index++)
+ sprintf(hex_hash + (index * 2), "%02x", output[index]);
+ hex_hash[SHA256_HEX_SIZE] = 0;
+ std::string ret = hex_hash;
+ return ret;
+}
+
+std::string HashPassword(const std::string& Password) {
+ const char* prefix = FBE_PERSONALIZATION;
+ return PersonalizedHash(prefix, Password);
+}
diff --git a/crypto/fscrypt/HashPassword.h b/crypto/fscrypt/HashPassword.h
new file mode 100644
index 0000000..73880b1
--- /dev/null
+++ b/crypto/fscrypt/HashPassword.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2016 Team Win Recovery Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __HASH_PASSWORD_H
+#define __HASH_PASSWORD_H
+
+#include <string>
+
+#define FBE_PERSONALIZATION "Android FBE credential hash"
+#define PERSONALISATION_WEAVER_KEY "weaver-key"
+#define PERSONALISATION_WEAVER_PASSWORD "weaver-pwd"
+#define PERSONALISATION_APPLICATION_ID "application-id"
+#define PERSONALIZATION_FBE_KEY "fbe-key"
+#define PERSONALIZATION_USER_GK_AUTH "user-gk-authentication"
+#define PERSONALISATION_SECDISCARDABLE "secdiscardable-transform"
+#define PERSONALISATION_CONTEXT "android-synthetic-password-personalization-context"
+
+void* PersonalizedHashBinary(const char* prefix, const char* key, const size_t key_size);
+
+std::string PersonalizedHash(const char* prefix, const char* key, const size_t key_size);
+std::string PersonalizedHash(const char* prefix, const std::string& Password);
+std::string PersonalizedHashSP800(const char* label, const char* context, const char* key, const size_t key_size);
+std::string HashPassword(const std::string& Password);
+
+template <class T>
+void endianswap(T *objp) {
+ unsigned char *memp = reinterpret_cast<unsigned char*>(objp);
+ std::reverse(memp, memp + sizeof(T));
+}
+
+#endif
diff --git a/crypto/fscrypt/KeyBuffer.cpp b/crypto/fscrypt/KeyBuffer.cpp
new file mode 100644
index 0000000..e7aede5
--- /dev/null
+++ b/crypto/fscrypt/KeyBuffer.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "KeyBuffer.h"
+
+#include <algorithm>
+#include <cstring>
+
+namespace android {
+namespace vold {
+
+KeyBuffer operator+(KeyBuffer&& lhs, const KeyBuffer& rhs) {
+ std::copy(rhs.begin(), rhs.end(), std::back_inserter(lhs));
+ return std::move(lhs);
+}
+
+KeyBuffer operator+(KeyBuffer&& lhs, const char* rhs) {
+ std::copy(rhs, rhs + strlen(rhs), std::back_inserter(lhs));
+ return std::move(lhs);
+}
+
+} // namespace vold
+} // namespace android
+
diff --git a/crypto/fscrypt/KeyBuffer.h b/crypto/fscrypt/KeyBuffer.h
new file mode 100644
index 0000000..2087187
--- /dev/null
+++ b/crypto/fscrypt/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/fscrypt/KeyStorage.cpp b/crypto/fscrypt/KeyStorage.cpp
new file mode 100755
index 0000000..c68daa1
--- /dev/null
+++ b/crypto/fscrypt/KeyStorage.cpp
@@ -0,0 +1,598 @@
+/*
+ * 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 "Checkpoint.h"
+
+#include <thread>
+#include <vector>
+
+#include <errno.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/sha.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <android-base/properties.h>
+
+#include <cutils/properties.h>
+
+#include <hardware/hw_auth_token.h>
+#include <keymasterV4_0/authorization_set.h>
+#include <keymasterV4_0/keymaster_utils.h>
+
+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
+
+static const char* kCurrentVersion = "1";
+static const char* kRmPath = "/system/bin/rm";
+static const char* kSecdiscardPath = "/system/bin/secdiscard";
+static const char* kStretch_none = "none";
+static const char* kStretch_nopassword = "nopassword";
+static const std::string kStretchPrefix_scrypt = "scrypt ";
+static const char* kHashPrefix_secdiscardable = "Android secdiscardable SHA512";
+static const char* kHashPrefix_keygen = "Android key wrapping key generation SHA512";
+static const char* kFn_encrypted_key = "encrypted_key";
+static const char* kFn_keymaster_key_blob = "keymaster_key_blob";
+static const char* kFn_keymaster_key_blob_upgraded = "keymaster_key_blob_upgraded";
+static const char* kFn_salt = "salt";
+static const char* kFn_secdiscardable = "secdiscardable";
+static const char* kFn_stretching = "stretching";
+static const char* kFn_version = "version";
+
+static bool checkSize(const std::string& kind, size_t actual, size_t expected) {
+ if (actual != expected) {
+ LOG(ERROR) << "Wrong number of bytes in " << kind << ", expected " << expected << " got "
+ << actual;
+ return false;
+ }
+ return true;
+}
+
+static void hashWithPrefix(char const* prefix, const std::string& tohash, std::string* res) {
+ SHA512_CTX c;
+
+ SHA512_Init(&c);
+ // Personalise the hashing by introducing a fixed prefix.
+ // Hashing applications should use personalization except when there is a
+ // specific reason not to; see section 4.11 of https://www.schneier.com/skein1.3.pdf
+ std::string hashingPrefix = prefix;
+ hashingPrefix.resize(SHA512_CBLOCK);
+ SHA512_Update(&c, hashingPrefix.data(), hashingPrefix.size());
+ SHA512_Update(&c, tohash.data(), tohash.size());
+ res->assign(SHA512_DIGEST_LENGTH, '\0');
+ SHA512_Final(reinterpret_cast<uint8_t*>(&(*res)[0]), &c);
+}
+
+static bool generateKeymasterKey(Keymaster& keymaster, const KeyAuthentication& auth,
+ const std::string& appId, std::string* key) {
+ auto paramBuilder = km::AuthorizationSetBuilder()
+ .AesEncryptionKey(AES_KEY_BYTES * 8)
+ .GcmModeMinMacLen(GCM_MAC_BYTES * 8)
+ .Authorization(km::TAG_APPLICATION_ID, km::support::blob2hidlVec(appId));
+ if (auth.token.empty()) {
+ LOG(DEBUG) << "Creating key that doesn't need auth token";
+ paramBuilder.Authorization(km::TAG_NO_AUTH_REQUIRED);
+ } else {
+ LOG(DEBUG) << "Auth token required for key";
+ if (auth.token.size() != sizeof(hw_auth_token_t)) {
+ LOG(ERROR) << "Auth token should be " << sizeof(hw_auth_token_t) << " bytes, was "
+ << auth.token.size() << " bytes";
+ return false;
+ }
+ const hw_auth_token_t* at = reinterpret_cast<const hw_auth_token_t*>(auth.token.data());
+ 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);
+}
+
+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";
+ authToken = km::support::hidlVec2AuthToken(km::support::blob2hidlVec(auth.token));
+ }
+ return {paramBuilder, authToken};
+}
+
+static bool readFileToString(const std::string& filename, std::string* result) {
+ if (!android::base::ReadFileToString(filename, result)) {
+ PLOG(ERROR) << "Failed to read from " << filename;
+ return false;
+ }
+ return true;
+}
+
+static bool readRandomBytesOrLog(size_t count, std::string* out) {
+ auto status = ReadRandomBytes(count, *out);
+ if (status != OK) {
+ LOG(ERROR) << "Random read failed with status: " << status;
+ return false;
+ }
+ return true;
+}
+
+bool createSecdiscardable(const std::string& filename, std::string* hash) {
+ std::string secdiscardable;
+ if (!readRandomBytesOrLog(SECDISCARDABLE_BYTES, &secdiscardable)) return false;
+ if (!writeStringToFile(secdiscardable, filename)) return false;
+ hashWithPrefix(kHashPrefix_secdiscardable, secdiscardable, hash);
+ return true;
+}
+
+bool readSecdiscardable(const std::string& filename, std::string* hash) {
+ std::string secdiscardable;
+ if (!readFileToString(filename, &secdiscardable)) return false;
+ hashWithPrefix(kHashPrefix_secdiscardable, secdiscardable, hash);
+ return true;
+}
+
+// static void deferedKmDeleteKey(const std::string& kmkey) {
+// while (!android::base::WaitForProperty("vold.checkpoint_committed", "1")) {
+// LOG(ERROR) << "Wait for boot timed out";
+// }
+// Keymaster keymaster;
+// if (!keymaster || !keymaster.deleteKey(kmkey)) {
+// LOG(ERROR) << "Defered Key deletion failed during upgrade";
+// }
+// }
+
+bool kmDeleteKey(Keymaster& keymaster, const std::string& kmKey) {
+ return true;
+ // bool needs_cp = cp_needsCheckpoint();
+
+ // if (needs_cp) {
+ // std::thread(deferedKmDeleteKey, kmKey).detach();
+ // LOG(INFO) << "Deferring Key deletion during upgrade";
+ // return true;
+ // } else {
+ // return keymaster.deleteKey(kmKey);
+ // }
+}
+
+static KeymasterOperation begin(Keymaster& keymaster, const std::string& dir,
+ km::KeyPurpose purpose, const km::AuthorizationSet& keyParams,
+ const km::AuthorizationSet& opParams,
+ const km::HardwareAuthToken& authToken,
+ km::AuthorizationSet* outParams, bool keepOld) {
+ auto kmKeyPath = dir + "/" + kFn_keymaster_key_blob;
+ std::string kmKey;
+ if (!readFileToString(kmKeyPath, &kmKey)) return KeymasterOperation();
+ km::AuthorizationSet inParams(keyParams);
+ inParams.append(opParams.begin(), opParams.end());
+ for (;;) {
+ auto opHandle = keymaster.begin(purpose, kmKey, inParams, authToken, outParams);
+ if (opHandle) {
+ return opHandle;
+ }
+ if (opHandle.errorCode() != km::ErrorCode::KEY_REQUIRES_UPGRADE) return opHandle;
+ LOG(DEBUG) << "Upgrading key: " << dir;
+ std::string newKey;
+ if (!keymaster.upgradeKey(kmKey, keyParams, &newKey)) return KeymasterOperation();
+ // auto newKeyPath = dir + "/" + kFn_keymaster_key_blob_upgraded;
+ // if (!writeStringToFile(newKey, newKeyPath)) return KeymasterOperation();
+ // if (!keepOld) {
+ // if (rename(newKeyPath.c_str(), kmKeyPath.c_str()) != 0) {
+ // PLOG(ERROR) << "Unable to move upgraded key to location: " << kmKeyPath;
+ // return KeymasterOperation();
+ // }
+ // if (!android::vold::FsyncDirectory(dir)) {
+ // LOG(ERROR) << "Key dir sync failed: " << dir;
+ // return KeymasterOperation();
+ // }
+ // if (!kmDeleteKey(keymaster, kmKey)) {
+ // LOG(ERROR) << "Key deletion failed during upgrade, continuing anyway: " << dir;
+ // }
+ // }
+ kmKey = newKey;
+ LOG(INFO) << "Key upgraded in memory: " << dir;
+ }
+}
+
+static bool encryptWithKeymasterKey(Keymaster& keymaster, const std::string& dir,
+ const km::AuthorizationSet& keyParams,
+ const km::HardwareAuthToken& authToken, const KeyBuffer& message,
+ std::string* ciphertext, bool keepOld) {
+ km::AuthorizationSet opParams;
+ km::AuthorizationSet outParams;
+ auto opHandle = begin(keymaster, dir, km::KeyPurpose::ENCRYPT, keyParams, opParams, authToken,
+ &outParams, keepOld);
+ if (!opHandle) return false;
+ auto nonceBlob = outParams.GetTagValue(km::TAG_NONCE);
+ if (!nonceBlob.isOk()) {
+ LOG(ERROR) << "GCM encryption but no nonce generated";
+ return false;
+ }
+ // nonceBlob here is just a pointer into existing data, must not be freed
+ std::string nonce(reinterpret_cast<const char*>(&nonceBlob.value()[0]),
+ nonceBlob.value().size());
+ if (!checkSize("nonce", nonce.size(), GCM_NONCE_BYTES)) return false;
+ std::string body;
+ if (!opHandle.updateCompletely(message, &body)) return false;
+
+ std::string mac;
+ if (!opHandle.finish(&mac)) return false;
+ if (!checkSize("mac", mac.size(), GCM_MAC_BYTES)) return false;
+ *ciphertext = nonce + body + mac;
+ return true;
+}
+
+static bool decryptWithKeymasterKey(Keymaster& keymaster, const std::string& dir,
+ const km::AuthorizationSet& keyParams,
+ const km::HardwareAuthToken& authToken,
+ const std::string& ciphertext, KeyBuffer* message,
+ bool keepOld) {
+ auto nonce = ciphertext.substr(0, GCM_NONCE_BYTES);
+ auto bodyAndMac = ciphertext.substr(GCM_NONCE_BYTES);
+ auto opParams = km::AuthorizationSetBuilder().Authorization(km::TAG_NONCE,
+ km::support::blob2hidlVec(nonce));
+ auto opHandle = begin(keymaster, dir, km::KeyPurpose::DECRYPT, keyParams, opParams, authToken,
+ nullptr, keepOld);
+ if (!opHandle) return false;
+ if (!opHandle.updateCompletely(bodyAndMac, message)) return false;
+ if (!opHandle.finish(nullptr)) return false;
+ return true;
+}
+
+static std::string getStretching(const KeyAuthentication& auth) {
+ if (!auth.usesKeymaster()) {
+ return kStretch_none;
+ } else if (auth.secret.empty()) {
+ return kStretch_nopassword;
+ } else {
+ char paramstr[PROPERTY_VALUE_MAX];
+
+ property_get(SCRYPT_PROP, paramstr, SCRYPT_DEFAULTS);
+ return std::string() + kStretchPrefix_scrypt + paramstr;
+ }
+}
+
+static bool stretchingNeedsSalt(const std::string& stretching) {
+ return stretching != kStretch_nopassword && stretching != kStretch_none;
+}
+
+static bool stretchSecret(const std::string& stretching, const std::string& secret,
+ const std::string& salt, std::string* stretched) {
+ if (stretching == kStretch_nopassword) {
+ if (!secret.empty()) {
+ LOG(WARNING) << "Password present but stretching is nopassword";
+ // Continue anyway
+ }
+ stretched->clear();
+ } else if (stretching == kStretch_none) {
+ *stretched = secret;
+ } else if (std::equal(kStretchPrefix_scrypt.begin(), kStretchPrefix_scrypt.end(),
+ stretching.begin())) {
+ int Nf, rf, pf;
+ if (!parse_scrypt_parameters(stretching.substr(kStretchPrefix_scrypt.size()).c_str(), &Nf,
+ &rf, &pf)) {
+ LOG(ERROR) << "Unable to parse scrypt params in stretching: " << stretching;
+ return false;
+ }
+ stretched->assign(STRETCHED_BYTES, '\0');
+ if (crypto_scrypt(reinterpret_cast<const uint8_t*>(secret.data()), secret.size(),
+ reinterpret_cast<const uint8_t*>(salt.data()), salt.size(), 1 << Nf,
+ 1 << rf, 1 << pf, reinterpret_cast<uint8_t*>(&(*stretched)[0]),
+ stretched->size()) != 0) {
+ LOG(ERROR) << "scrypt failed with params: " << stretching;
+ return false;
+ }
+ } else {
+ LOG(ERROR) << "Unknown stretching type: " << stretching;
+ return false;
+ }
+ return true;
+}
+
+static bool generateAppId(const KeyAuthentication& auth, const std::string& stretching,
+ const std::string& salt, const std::string& secdiscardable_hash,
+ std::string* appId) {
+ std::string stretched;
+ if (!stretchSecret(stretching, auth.secret, salt, &stretched)) return false;
+ *appId = secdiscardable_hash + stretched;
+ return true;
+}
+
+static void logOpensslError() {
+ LOG(ERROR) << "Openssl error: " << ERR_get_error();
+}
+
+static bool encryptWithoutKeymaster(const std::string& preKey, const KeyBuffer& plaintext,
+ std::string* ciphertext) {
+ std::string key;
+ hashWithPrefix(kHashPrefix_keygen, preKey, &key);
+ key.resize(AES_KEY_BYTES);
+ if (!readRandomBytesOrLog(GCM_NONCE_BYTES, ciphertext)) return false;
+ auto ctx = std::unique_ptr<EVP_CIPHER_CTX, decltype(&::EVP_CIPHER_CTX_free)>(
+ EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free);
+ if (!ctx) {
+ logOpensslError();
+ return false;
+ }
+ if (1 != EVP_EncryptInit_ex(ctx.get(), EVP_aes_256_gcm(), NULL,
+ reinterpret_cast<const uint8_t*>(key.data()),
+ reinterpret_cast<const uint8_t*>(ciphertext->data()))) {
+ logOpensslError();
+ return false;
+ }
+ ciphertext->resize(GCM_NONCE_BYTES + plaintext.size() + GCM_MAC_BYTES);
+ int outlen;
+ if (1 != EVP_EncryptUpdate(
+ ctx.get(), reinterpret_cast<uint8_t*>(&(*ciphertext)[0] + GCM_NONCE_BYTES),
+ &outlen, reinterpret_cast<const uint8_t*>(plaintext.data()), plaintext.size())) {
+ logOpensslError();
+ return false;
+ }
+ if (outlen != static_cast<int>(plaintext.size())) {
+ LOG(ERROR) << "GCM ciphertext length should be " << plaintext.size() << " was " << outlen;
+ return false;
+ }
+ if (1 != EVP_EncryptFinal_ex(
+ ctx.get(),
+ reinterpret_cast<uint8_t*>(&(*ciphertext)[0] + GCM_NONCE_BYTES + plaintext.size()),
+ &outlen)) {
+ logOpensslError();
+ return false;
+ }
+ if (outlen != 0) {
+ LOG(ERROR) << "GCM EncryptFinal should be 0, was " << outlen;
+ return false;
+ }
+ if (1 != EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_GET_TAG, GCM_MAC_BYTES,
+ reinterpret_cast<uint8_t*>(&(*ciphertext)[0] + GCM_NONCE_BYTES +
+ plaintext.size()))) {
+ logOpensslError();
+ return false;
+ }
+ return true;
+}
+
+static bool decryptWithoutKeymaster(const std::string& preKey, const std::string& ciphertext,
+ KeyBuffer* plaintext) {
+ if (ciphertext.size() < GCM_NONCE_BYTES + GCM_MAC_BYTES) {
+ LOG(ERROR) << "GCM ciphertext too small: " << ciphertext.size();
+ return false;
+ }
+ std::string key;
+ hashWithPrefix(kHashPrefix_keygen, preKey, &key);
+ key.resize(AES_KEY_BYTES);
+ auto ctx = std::unique_ptr<EVP_CIPHER_CTX, decltype(&::EVP_CIPHER_CTX_free)>(
+ EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free);
+ if (!ctx) {
+ logOpensslError();
+ return false;
+ }
+ if (1 != EVP_DecryptInit_ex(ctx.get(), EVP_aes_256_gcm(), NULL,
+ reinterpret_cast<const uint8_t*>(key.data()),
+ reinterpret_cast<const uint8_t*>(ciphertext.data()))) {
+ logOpensslError();
+ return false;
+ }
+ *plaintext = KeyBuffer(ciphertext.size() - GCM_NONCE_BYTES - GCM_MAC_BYTES);
+ int outlen;
+ if (1 != EVP_DecryptUpdate(ctx.get(), reinterpret_cast<uint8_t*>(&(*plaintext)[0]), &outlen,
+ reinterpret_cast<const uint8_t*>(ciphertext.data() + GCM_NONCE_BYTES),
+ plaintext->size())) {
+ logOpensslError();
+ return false;
+ }
+ if (outlen != static_cast<int>(plaintext->size())) {
+ LOG(ERROR) << "GCM plaintext length should be " << plaintext->size() << " was " << outlen;
+ return false;
+ }
+ if (1 != EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_SET_TAG, GCM_MAC_BYTES,
+ const_cast<void*>(reinterpret_cast<const void*>(
+ ciphertext.data() + GCM_NONCE_BYTES + plaintext->size())))) {
+ logOpensslError();
+ return false;
+ }
+ if (1 != EVP_DecryptFinal_ex(ctx.get(),
+ reinterpret_cast<uint8_t*>(&(*plaintext)[0] + plaintext->size()),
+ &outlen)) {
+ logOpensslError();
+ return false;
+ }
+ if (outlen != 0) {
+ LOG(ERROR) << "GCM EncryptFinal should be 0, was " << outlen;
+ return false;
+ }
+ return true;
+}
+
+bool pathExists(const std::string& path) {
+ return access(path.c_str(), F_OK) == 0;
+}
+
+bool storeKey(const std::string& dir, const KeyAuthentication& auth, const KeyBuffer& key) {
+ if (TEMP_FAILURE_RETRY(mkdir(dir.c_str(), 0700)) == -1) {
+ PLOG(ERROR) << "key mkdir " << dir;
+ return false;
+ }
+ if (!writeStringToFile(kCurrentVersion, dir + "/" + kFn_version)) return false;
+ std::string secdiscardable_hash;
+ if (!createSecdiscardable(dir + "/" + kFn_secdiscardable, &secdiscardable_hash)) return false;
+ std::string stretching = getStretching(auth);
+ if (!writeStringToFile(stretching, dir + "/" + kFn_stretching)) return false;
+ std::string salt;
+ if (stretchingNeedsSalt(stretching)) {
+ if (ReadRandomBytes(SALT_BYTES, salt) != OK) {
+ LOG(ERROR) << "Random read failed";
+ return false;
+ }
+ if (!writeStringToFile(salt, dir + "/" + kFn_salt)) return false;
+ }
+ std::string appId;
+ if (!generateAppId(auth, stretching, salt, secdiscardable_hash, &appId)) return false;
+ std::string encryptedKey;
+ if (auth.usesKeymaster()) {
+ Keymaster keymaster;
+ if (!keymaster) return false;
+ std::string kmKey;
+ if (!generateKeymasterKey(keymaster, auth, appId, &kmKey)) return false;
+ if (!writeStringToFile(kmKey, dir + "/" + kFn_keymaster_key_blob)) return false;
+ km::AuthorizationSet keyParams;
+ km::HardwareAuthToken authToken;
+ std::tie(keyParams, authToken) = beginParams(auth, appId);
+ if (!encryptWithKeymasterKey(keymaster, dir, keyParams, authToken, key, &encryptedKey,
+ false))
+ return false;
+ } else {
+ if (!encryptWithoutKeymaster(appId, key, &encryptedKey)) return false;
+ }
+ if (!writeStringToFile(encryptedKey, dir + "/" + kFn_encrypted_key)) return false;
+ if (!FsyncDirectory(dir)) return false;
+ return true;
+}
+
+bool storeKeyAtomically(const std::string& key_path, const std::string& tmp_path,
+ const KeyAuthentication& auth, const KeyBuffer& key) {
+ if (pathExists(key_path)) {
+ LOG(ERROR) << "Already exists, cannot create key at: " << key_path;
+ return false;
+ }
+ if (pathExists(tmp_path)) {
+ LOG(DEBUG) << "Already exists, destroying: " << tmp_path;
+ destroyKey(tmp_path); // May be partially created so ignore errors
+ }
+ if (!storeKey(tmp_path, auth, key)) return false;
+ if (rename(tmp_path.c_str(), key_path.c_str()) != 0) {
+ PLOG(ERROR) << "Unable to move new key to location: " << key_path;
+ return false;
+ }
+ LOG(DEBUG) << "Created key: " << key_path;
+ return true;
+}
+
+bool retrieveKey(const std::string& dir, const KeyAuthentication& auth, KeyBuffer* key,
+ bool keepOld) {
+ std::string version;
+ if (!readFileToString(dir + "/" + kFn_version, &version)) return false;
+ if (version != kCurrentVersion) {
+ LOG(ERROR) << "Version mismatch, expected " << kCurrentVersion << " got " << version;
+ return false;
+ }
+ std::string secdiscardable_hash;
+ if (!readSecdiscardable(dir + "/" + kFn_secdiscardable, &secdiscardable_hash)) return false;
+ std::string stretching;
+ if (!readFileToString(dir + "/" + kFn_stretching, &stretching)) return false;
+ std::string salt;
+ if (stretchingNeedsSalt(stretching)) {
+ if (!readFileToString(dir + "/" + kFn_salt, &salt)) return false;
+ }
+ std::string appId;
+ if (!generateAppId(auth, stretching, salt, secdiscardable_hash, &appId)) return false;
+ std::string encryptedMessage;
+ if (!readFileToString(dir + "/" + kFn_encrypted_key, &encryptedMessage)) return false;
+ if (auth.usesKeymaster()) {
+ Keymaster keymaster;
+ if (!keymaster) return false;
+ km::AuthorizationSet keyParams;
+ km::HardwareAuthToken authToken;
+ std::tie(keyParams, authToken) = beginParams(auth, appId);
+ if (!decryptWithKeymasterKey(keymaster, dir, keyParams, authToken, encryptedMessage, key,
+ keepOld))
+ return false;
+ } else {
+ if (!decryptWithoutKeymaster(appId, encryptedMessage, key)) return false;
+ }
+ return true;
+}
+
+static bool deleteKey(const std::string& dir) {
+ std::string kmKey;
+ if (!readFileToString(dir + "/" + kFn_keymaster_key_blob, &kmKey)) return false;
+ Keymaster keymaster;
+ if (!keymaster) return false;
+ if (!keymaster.deleteKey(kmKey)) return false;
+ return true;
+}
+
+bool runSecdiscardSingle(const std::string& file) {
+ if (ForkExecvp(std::vector<std::string>{kSecdiscardPath, "--", file}) != 0) {
+ LOG(ERROR) << "secdiscard failed";
+ return false;
+ }
+ return true;
+}
+
+static bool recursiveDeleteKey(const std::string& dir) {
+ if (ForkExecvp(std::vector<std::string>{kRmPath, "-rf", dir}) != 0) {
+ LOG(ERROR) << "recursive delete failed";
+ return false;
+ }
+ return true;
+}
+
+bool destroyKey(const std::string& dir) {
+ bool success = true;
+ // Try each thing, even if previous things failed.
+ bool uses_km = pathExists(dir + "/" + kFn_keymaster_key_blob);
+ if (uses_km) {
+ success &= deleteKey(dir);
+ }
+ auto secdiscard_cmd = std::vector<std::string>{
+ kSecdiscardPath,
+ "--",
+ dir + "/" + kFn_encrypted_key,
+ dir + "/" + kFn_secdiscardable,
+ };
+ if (uses_km) {
+ secdiscard_cmd.emplace_back(dir + "/" + kFn_keymaster_key_blob);
+ }
+ if (ForkExecvp(secdiscard_cmd) != 0) {
+ LOG(ERROR) << "secdiscard failed";
+ success = false;
+ }
+ success &= recursiveDeleteKey(dir);
+ return success;
+}
+
+} // namespace vold
+} // namespace android
diff --git a/crypto/fscrypt/KeyStorage.h b/crypto/fscrypt/KeyStorage.h
new file mode 100755
index 0000000..276b6b9
--- /dev/null
+++ b/crypto/fscrypt/KeyStorage.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_VOLD_KEYSTORAGE_H
+#define ANDROID_VOLD_KEYSTORAGE_H
+
+#include "KeyBuffer.h"
+
+#include <string>
+
+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(const std::string& t, const std::string& s) : token{t}, secret{s} {};
+
+ bool usesKeymaster() const { return !token.empty() || secret.empty(); };
+
+ const std::string token;
+ const std::string secret;
+};
+
+extern const KeyAuthentication kEmptyAuthentication;
+
+// Checks if path "path" exists.
+bool pathExists(const std::string& path);
+
+bool createSecdiscardable(const std::string& path, std::string* hash);
+bool readSecdiscardable(const std::string& path, std::string* hash);
+
+// Create a directory at the named path, and store "key" in it,
+// in such a way that it can only be retrieved via Keymaster and
+// can be securely deleted.
+// It's safe to move/rename the directory after creation.
+bool storeKey(const std::string& dir, const KeyAuthentication& auth, const KeyBuffer& key);
+
+// Create a directory at the named path, and store "key" in it as storeKey
+// This version creates the key in "tmp_path" then atomically renames "tmp_path"
+// to "key_path" thereby ensuring that the key is either stored entirely or
+// not at all.
+bool storeKeyAtomically(const std::string& key_path, const std::string& tmp_path,
+ const KeyAuthentication& auth, const KeyBuffer& key);
+
+// Retrieve the key from the named directory.
+bool retrieveKey(const std::string& dir, const KeyAuthentication& auth, KeyBuffer* key,
+ bool keepOld = false);
+
+// 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/fscrypt/KeyUtil.cpp b/crypto/fscrypt/KeyUtil.cpp
new file mode 100755
index 0000000..91d6b37
--- /dev/null
+++ b/crypto/fscrypt/KeyUtil.cpp
@@ -0,0 +1,191 @@
+/*
+ * 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 <linux/fs.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 "KeyStorage.h"
+#include "Utils.h"
+
+namespace android {
+namespace vold {
+
+constexpr int FS_AES_256_XTS_KEY_SIZE = 64;
+
+bool randomKey(KeyBuffer* key) {
+ *key = KeyBuffer(FS_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";
+ return false;
+ }
+ return true;
+}
+
+// Get raw keyref - used to make keyname and to pass to ioctl
+static std::string generateKeyRef(const uint8_t* key, int length) {
+ SHA512_CTX c;
+
+ SHA512_Init(&c);
+ SHA512_Update(&c, key, length);
+ unsigned char key_ref1[SHA512_DIGEST_LENGTH];
+ SHA512_Final(key_ref1, &c);
+
+ SHA512_Init(&c);
+ SHA512_Update(&c, key_ref1, SHA512_DIGEST_LENGTH);
+ unsigned char key_ref2[SHA512_DIGEST_LENGTH];
+ SHA512_Final(key_ref2, &c);
+
+ static_assert(FS_KEY_DESCRIPTOR_SIZE <= SHA512_DIGEST_LENGTH, "Hash too short for descriptor");
+ return std::string((char*)key_ref2, FS_KEY_DESCRIPTOR_SIZE);
+}
+
+static bool fillKey(const KeyBuffer& key, fscrypt_key* fs_key) {
+ if (key.size() != FS_AES_256_XTS_KEY_SIZE) {
+ LOG(ERROR) << "Wrong size key " << key.size();
+ return false;
+ }
+ static_assert(FS_AES_256_XTS_KEY_SIZE <= sizeof(fs_key->raw), "Key too long!");
+ fs_key->mode = FS_ENCRYPTION_MODE_AES_256_XTS;
+ fs_key->size = key.size();
+ memset(fs_key->raw, 0, sizeof(fs_key->raw));
+ memcpy(fs_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 fscryptKeyring(key_serial_t* device_keyring) {
+ *device_keyring = keyctl_search(KEY_SPEC_SESSION_KEYRING, "keyring", "fscrypt", 0);
+ if (*device_keyring == -1) {
+ PLOG(ERROR) << "Unable to find device keyring";
+ return false;
+ }
+ return true;
+}
+
+// Install password into global keyring
+// Return raw key reference for use in policy
+bool installKey(const KeyBuffer& key, std::string* raw_ref) {
+ // Place fscrypt_key into automatically zeroing buffer.
+ KeyBuffer fsKeyBuffer(sizeof(fscrypt_key));
+ fscrypt_key& fs_key = *reinterpret_cast<fscrypt_key*>(fsKeyBuffer.data());
+
+ if (!fillKey(key, &fs_key)) return false;
+ *raw_ref = generateKeyRef(fs_key.raw, fs_key.size);
+ key_serial_t device_keyring;
+ if (!fscryptKeyring(&device_keyring)) return false;
+ for (char const* const* name_prefix = NAME_PREFIXES; *name_prefix != nullptr; name_prefix++) {
+ auto ref = keyname(*name_prefix, *raw_ref);
+ key_serial_t key_id =
+ add_key("logon", ref.c_str(), (void*)&fs_key, sizeof(fs_key), device_keyring);
+ if (key_id == -1) {
+ PLOG(ERROR) << "Failed to insert key into keyring " << device_keyring;
+ return false;
+ }
+ LOG(DEBUG) << "Added key " << key_id << " (" << ref << ") to keyring " << device_keyring
+ << " in process " << getpid();
+ }
+ return true;
+}
+
+bool evictKey(const std::string& raw_ref) {
+ key_serial_t device_keyring;
+ if (!fscryptKeyring(&device_keyring)) return false;
+ bool success = true;
+ for (char const* const* name_prefix = NAME_PREFIXES; *name_prefix != nullptr; name_prefix++) {
+ auto ref = 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) {
+ KeyBuffer key;
+ if (pathExists(key_path)) {
+ LOG(DEBUG) << "Key exists, using: " << key_path;
+ if (!retrieveKey(key_path, key_authentication, &key)) return false;
+ } else {
+ if (!create_if_absent) {
+ LOG(ERROR) << "No key found in " << key_path;
+ return false;
+ }
+ LOG(INFO) << "Creating new key in " << key_path;
+ if (!randomKey(&key)) return false;
+ if (!storeKeyAtomically(key_path, tmp_path, key_authentication, key)) return false;
+ }
+
+ if (!installKey(key, key_ref)) {
+ LOG(ERROR) << "Failed to install key in " << key_path;
+ return false;
+ }
+ return true;
+}
+
+bool retrieveKey(bool create_if_absent, const std::string& key_path, const std::string& tmp_path,
+ KeyBuffer* key, bool keepOld) {
+ LOG(ERROR) << "retreiveKey1";
+ if (pathExists(key_path)) {
+ LOG(ERROR) << "Key exists, using: " << key_path;
+ if (!retrieveKey(key_path, kEmptyAuthentication, key, keepOld)) return false;
+ } else {
+ if (!create_if_absent) {
+ LOG(ERROR) << "No key found in " << key_path;
+ return false;
+ }
+ LOG(ERROR) << "Creating new key in " << key_path;
+ if (!randomKey(key)) return false;
+ LOG(ERROR) << "retrieveKey1";
+ if (!storeKeyAtomically(key_path, tmp_path, kEmptyAuthentication, *key)) return false;
+ }
+ return true;
+}
+
+} // namespace vold
+} // namespace android
diff --git a/crypto/fscrypt/KeyUtil.h b/crypto/fscrypt/KeyUtil.h
new file mode 100755
index 0000000..7ee6725
--- /dev/null
+++ b/crypto/fscrypt/KeyUtil.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_VOLD_KEYUTIL_H
+#define ANDROID_VOLD_KEYUTIL_H
+
+#include "KeyBuffer.h"
+#include "KeyStorage.h"
+
+#include <memory>
+#include <string>
+
+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 retrieveKey(bool create_if_absent, const std::string& key_path, const std::string& tmp_path,
+ KeyBuffer* key, bool keepOld = true);
+
+} // namespace vold
+} // namespace android
+
+#endif
diff --git a/crypto/fscrypt/Keymaster.cpp b/crypto/fscrypt/Keymaster.cpp
new file mode 100755
index 0000000..aad4387
--- /dev/null
+++ b/crypto/fscrypt/Keymaster.cpp
@@ -0,0 +1,346 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Keymaster.h"
+
+#include <android-base/logging.h>
+#include <keymasterV4_0/authorization_set.h>
+#include <keymasterV4_0/keymaster_utils.h>
+
+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();
+ mDevice = nullptr;
+ return false;
+ }
+ if (km_error != km::ErrorCode::OK) {
+ LOG(ERROR) << "update failed, code " << int32_t(km_error);
+ mDevice = nullptr;
+ return false;
+ }
+ if (inputConsumed > inputLen) {
+ LOG(ERROR) << "update reported too much input consumed";
+ mDevice = nullptr;
+ return false;
+ }
+ }
+ return true;
+}
+
+bool KeymasterOperation::finish(std::string* output) {
+ km::ErrorCode km_error;
+ auto hidlCb = [&](km::ErrorCode ret, const hidl_vec<km::KeyParameter>& /*ignored*/,
+ const hidl_vec<uint8_t>& _output) {
+ km_error = ret;
+ if (km_error != km::ErrorCode::OK) return;
+ if (output) output->assign(reinterpret_cast<const char*>(&_output[0]), _output.size());
+ };
+ auto error = mDevice->finish(mOpHandle, hidl_vec<km::KeyParameter>(), hidl_vec<uint8_t>(),
+ hidl_vec<uint8_t>(), km::HardwareAuthToken(),
+ km::VerificationToken(), hidlCb);
+ mDevice = nullptr;
+ if (!error.isOk()) {
+ LOG(ERROR) << "finish failed: " << error.description();
+ return false;
+ }
+ if (km_error != km::ErrorCode::OK) {
+ LOG(ERROR) << "finish failed, code " << int32_t(km_error);
+ return false;
+ }
+ return true;
+}
+
+/* static */ bool Keymaster::hmacKeyGenerated = false;
+
+Keymaster::Keymaster() {
+ auto devices = KmDevice::enumerateAvailableDevices();
+ if (!hmacKeyGenerated) {
+ KmDevice::performHmacKeyAgreement(devices);
+ hmacKeyGenerated = true;
+ }
+ for (auto& dev : devices) {
+ // Do not use StrongBox for device encryption / credential encryption. If a security chip
+ // is present it will have Weaver, which already strengthens CE. We get no additional
+ // benefit from using StrongBox here, so skip it.
+ if (dev->halVersion().securityLevel != SecurityLevel::STRONGBOX) {
+ mDevice = std::move(dev);
+ break;
+ }
+ }
+ if (!mDevice) return;
+ auto& version = mDevice->halVersion();
+ LOG(INFO) << "Using " << version.keymasterName << " from " << version.authorName
+ << " for encryption. Security level: " << toString(version.securityLevel)
+ << ", HAL: " << mDevice->descriptor() << "/" << mDevice->instanceName();
+}
+
+bool Keymaster::generateKey(const km::AuthorizationSet& inParams, std::string* key) {
+ km::ErrorCode km_error;
+ auto hidlCb = [&](km::ErrorCode ret, const hidl_vec<uint8_t>& keyBlob,
+ const km::KeyCharacteristics& /*ignored*/) {
+ km_error = ret;
+ if (km_error != km::ErrorCode::OK) return;
+ if (key) key->assign(reinterpret_cast<const char*>(&keyBlob[0]), keyBlob.size());
+ };
+
+ auto error = mDevice->generateKey(inParams.hidl_data(), hidlCb);
+ if (!error.isOk()) {
+ LOG(ERROR) << "generate_key failed: " << error.description();
+ return false;
+ }
+ if (km_error != km::ErrorCode::OK) {
+ LOG(ERROR) << "generate_key failed, code " << int32_t(km_error);
+ return false;
+ }
+ return true;
+}
+
+bool Keymaster::deleteKey(const std::string& key) {
+ auto keyBlob = km::support::blob2hidlVec(key);
+ auto error = mDevice->deleteKey(keyBlob);
+ if (!error.isOk()) {
+ LOG(ERROR) << "delete_key failed: " << error.description();
+ return false;
+ }
+ if (error != km::ErrorCode::OK) {
+ LOG(ERROR) << "delete_key failed, code " << int32_t(km::ErrorCode(error));
+ return false;
+ }
+ return true;
+}
+
+bool Keymaster::upgradeKey(const std::string& oldKey, const km::AuthorizationSet& inParams,
+ std::string* newKey) {
+ auto oldKeyBlob = km::support::blob2hidlVec(oldKey);
+ km::ErrorCode km_error;
+ auto hidlCb = [&](km::ErrorCode ret, const hidl_vec<uint8_t>& upgradedKeyBlob) {
+ km_error = ret;
+ if (km_error != km::ErrorCode::OK) return;
+ if (newKey)
+ newKey->assign(reinterpret_cast<const char*>(&upgradedKeyBlob[0]),
+ upgradedKeyBlob.size());
+ };
+ auto error = mDevice->upgradeKey(oldKeyBlob, inParams.hidl_data(), hidlCb);
+ if (!error.isOk()) {
+ LOG(ERROR) << "upgrade_key failed: " << error.description();
+ return false;
+ }
+ if (km_error != km::ErrorCode::OK) {
+ LOG(ERROR) << "upgrade_key failed, code " << int32_t(km_error);
+ return false;
+ }
+ return true;
+}
+
+KeymasterOperation Keymaster::begin(km::KeyPurpose purpose, const std::string& key,
+ const km::AuthorizationSet& inParams,
+ const km::HardwareAuthToken& authToken,
+ km::AuthorizationSet* outParams) {
+ auto keyBlob = km::support::blob2hidlVec(key);
+ uint64_t mOpHandle;
+ km::ErrorCode km_error;
+
+ auto hidlCb = [&](km::ErrorCode ret, const hidl_vec<km::KeyParameter>& _outParams,
+ uint64_t operationHandle) {
+ km_error = ret;
+ if (km_error != km::ErrorCode::OK) return;
+ if (outParams) *outParams = _outParams;
+ mOpHandle = operationHandle;
+ };
+
+ auto error = mDevice->begin(purpose, keyBlob, inParams.hidl_data(), authToken, hidlCb);
+ if (!error.isOk()) {
+ LOG(ERROR) << "begin failed: " << error.description();
+ return KeymasterOperation(km::ErrorCode::UNKNOWN_ERROR);
+ }
+ if (km_error != km::ErrorCode::OK) {
+ LOG(ERROR) << "begin failed, code " << int32_t(km_error);
+ return KeymasterOperation(km_error);
+ }
+ return KeymasterOperation(mDevice.get(), mOpHandle);
+}
+
+bool Keymaster::isSecure() {
+ return mDevice->halVersion().securityLevel != km::SecurityLevel::SOFTWARE;
+}
+
+} // 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();
+}
+
+static bool write_string_to_buf(const std::string& towrite, uint8_t* buffer, uint32_t buffer_size,
+ uint32_t* out_size) {
+ if (!buffer || !out_size) {
+ LOG(ERROR) << "Missing target pointers";
+ return false;
+ }
+ *out_size = towrite.size();
+ if (buffer_size < towrite.size()) {
+ LOG(ERROR) << "Buffer too small " << buffer_size << " < " << towrite.size();
+ return false;
+ }
+ memset(buffer, '\0', buffer_size);
+ std::copy(towrite.begin(), towrite.end(), buffer);
+ return true;
+}
+
+static km::AuthorizationSet keyParams(uint32_t rsa_key_size, uint64_t rsa_exponent,
+ uint32_t ratelimit) {
+ return km::AuthorizationSetBuilder()
+ .RsaSigningKey(rsa_key_size, rsa_exponent)
+ .NoDigestOrPadding()
+ .Authorization(km::TAG_BLOB_USAGE_REQUIREMENTS, km::KeyBlobUsageRequirements::STANDALONE)
+ .Authorization(km::TAG_NO_AUTH_REQUIRED)
+ .Authorization(km::TAG_MIN_SECONDS_BETWEEN_OPS, ratelimit);
+}
+
+int keymaster_create_key_for_cryptfs_scrypt(uint32_t rsa_key_size, uint64_t rsa_exponent,
+ uint32_t ratelimit, uint8_t* key_buffer,
+ uint32_t key_buffer_size, uint32_t* key_out_size) {
+ if (key_out_size) {
+ *key_out_size = 0;
+ }
+ Keymaster dev;
+ if (!dev) {
+ LOG(ERROR) << "Failed to initiate keymaster session";
+ return -1;
+ }
+ std::string key;
+ if (!dev.generateKey(keyParams(rsa_key_size, rsa_exponent, ratelimit), &key)) return -1;
+ if (!write_string_to_buf(key, key_buffer, key_buffer_size, key_out_size)) return -1;
+ return 0;
+}
+
+int keymaster_upgrade_key_for_cryptfs_scrypt(uint32_t rsa_key_size, uint64_t rsa_exponent,
+ uint32_t ratelimit, const uint8_t* key_blob,
+ size_t key_blob_size, uint8_t* key_buffer,
+ uint32_t key_buffer_size, uint32_t* key_out_size) {
+ if (key_out_size) {
+ *key_out_size = 0;
+ }
+ Keymaster dev;
+ if (!dev) {
+ LOG(ERROR) << "Failed to initiate keymaster session";
+ return -1;
+ }
+ std::string old_key(reinterpret_cast<const char*>(key_blob), key_blob_size);
+ std::string new_key;
+ if (!dev.upgradeKey(old_key, keyParams(rsa_key_size, rsa_exponent, ratelimit), &new_key))
+ return -1;
+ if (!write_string_to_buf(new_key, key_buffer, key_buffer_size, key_out_size)) return -1;
+ return 0;
+}
+
+KeymasterSignResult keymaster_sign_object_for_cryptfs_scrypt(
+ const uint8_t* key_blob, size_t key_blob_size, uint32_t ratelimit, const uint8_t* object,
+ const size_t object_size, uint8_t** signature_buffer, size_t* signature_buffer_size) {
+ Keymaster dev;
+ if (!dev) {
+ LOG(ERROR) << "Failed to initiate keymaster session";
+ return KeymasterSignResult::error;
+ }
+ if (!key_blob || !object || !signature_buffer || !signature_buffer_size) {
+ LOG(ERROR) << __FILE__ << ":" << __LINE__ << ":Invalid argument";
+ return KeymasterSignResult::error;
+ }
+
+ km::AuthorizationSet outParams;
+ std::string key(reinterpret_cast<const char*>(key_blob), key_blob_size);
+ std::string input(reinterpret_cast<const char*>(object), object_size);
+ std::string output;
+ KeymasterOperation op;
+
+ auto paramBuilder = km::AuthorizationSetBuilder().NoDigestOrPadding();
+ while (true) {
+ op = dev.begin(km::KeyPurpose::SIGN, key, paramBuilder, km::HardwareAuthToken(), &outParams);
+ if (op.errorCode() == km::ErrorCode::KEY_RATE_LIMIT_EXCEEDED) {
+ sleep(ratelimit);
+ continue;
+ } else
+ break;
+ }
+
+ if (op.errorCode() == km::ErrorCode::KEY_REQUIRES_UPGRADE) {
+ LOG(ERROR) << "Keymaster key requires upgrade";
+ return KeymasterSignResult::upgrade;
+ }
+
+ if (op.errorCode() != km::ErrorCode::OK) {
+ LOG(ERROR) << "Error starting keymaster signature transaction: " << int32_t(op.errorCode());
+ return KeymasterSignResult::error;
+ }
+
+ if (!op.updateCompletely(input, &output)) {
+ LOG(ERROR) << "Error sending data to keymaster signature transaction: "
+ << uint32_t(op.errorCode());
+ return KeymasterSignResult::error;
+ }
+
+ if (!op.finish(&output)) {
+ LOG(ERROR) << "Error finalizing keymaster signature transaction: "
+ << int32_t(op.errorCode());
+ return KeymasterSignResult::error;
+ }
+
+ *signature_buffer = reinterpret_cast<uint8_t*>(malloc(output.size()));
+ if (*signature_buffer == nullptr) {
+ LOG(ERROR) << "Error allocation buffer for keymaster signature";
+ return KeymasterSignResult::error;
+ }
+ *signature_buffer_size = output.size();
+ std::copy(output.data(), output.data() + output.size(), *signature_buffer);
+ return KeymasterSignResult::ok;
+}
diff --git a/crypto/fscrypt/Keymaster.h b/crypto/fscrypt/Keymaster.h
new file mode 100644
index 0000000..42a2b5d
--- /dev/null
+++ b/crypto/fscrypt/Keymaster.h
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_VOLD_KEYMASTER_H
+#define ANDROID_VOLD_KEYMASTER_H
+
+#include "KeyBuffer.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include <android-base/macros.h>
+#include <keymasterV4_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() const { return mError == km::ErrorCode::OK; }
+ km::ErrorCode errorCode() const { return mError; }
+ // Call "update" repeatedly until all of the input is consumed, and
+ // concatenate the output. Return true on success.
+ template <class TI, class TO>
+ bool updateCompletely(TI& input, TO* output) {
+ if (output) output->clear();
+ return updateCompletely(input.data(), input.size(), [&](const char* b, size_t n) {
+ if (output) std::copy(b, b + n, std::back_inserter(*output));
+ });
+ }
+
+ // Finish and write the output to this string, unless pointer is null.
+ bool finish(std::string* output);
+ // Move constructor
+ KeymasterOperation(KeymasterOperation&& rhs) { *this = std::move(rhs); }
+ // Construct an object in an error state for error returns
+ KeymasterOperation() : mDevice{nullptr}, mOpHandle{0}, mError{km::ErrorCode::UNKNOWN_ERROR} {}
+ // Move Assignment
+ KeymasterOperation& operator=(KeymasterOperation&& rhs) {
+ mDevice = rhs.mDevice;
+ rhs.mDevice = nullptr;
+
+ mOpHandle = rhs.mOpHandle;
+ rhs.mOpHandle = 0;
+
+ mError = rhs.mError;
+ rhs.mError = km::ErrorCode::UNKNOWN_ERROR;
+
+ return *this;
+ }
+
+ private:
+ KeymasterOperation(KmDevice* d, uint64_t h)
+ : mDevice{d}, mOpHandle{h}, mError{km::ErrorCode::OK} {}
+ KeymasterOperation(km::ErrorCode error) : mDevice{nullptr}, mOpHandle{0}, mError{error} {}
+
+ bool updateCompletely(const char* input, size_t inputLen,
+ const std::function<void(const char*, size_t)> consumer);
+
+ KmDevice* mDevice;
+ uint64_t mOpHandle;
+ km::ErrorCode mError;
+ DISALLOW_COPY_AND_ASSIGN(KeymasterOperation);
+ friend class Keymaster;
+};
+
+// Wrapper for a Keymaster device for methods that start a KeymasterOperation or are not
+// part of one.
+class Keymaster {
+ public:
+ Keymaster();
+ // false if we failed to open the keymaster device.
+ explicit operator bool() { return mDevice.get() != nullptr; }
+ // Generate a key in the keymaster from the given params.
+ bool generateKey(const km::AuthorizationSet& inParams, std::string* key);
+ // 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/fscrypt/MetadataCrypt.cpp b/crypto/fscrypt/MetadataCrypt.cpp
new file mode 100755
index 0000000..45f3af3
--- /dev/null
+++ b/crypto/fscrypt/MetadataCrypt.cpp
@@ -0,0 +1,304 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "MetadataCrypt.h"
+#include "KeyBuffer.h"
+
+#include <algorithm>
+#include <string>
+#include <thread>
+#include <vector>
+
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <linux/dm-ioctl.h>
+
+#include <android-base/file.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 "Checkpoint.h"
+#include "EncryptInplace.h"
+#include "KeyStorage.h"
+#include "KeyUtil.h"
+#include "Keymaster.h"
+#include "Utils.h"
+#include "VoldUtil.h"
+
+#define DM_CRYPT_BUF_SIZE 4096
+#define TABLE_LOAD_RETRIES 10
+#define DEFAULT_KEY_TARGET_TYPE "default-key"
+
+using android::fs_mgr::FstabEntry;
+using android::fs_mgr::GetEntryForMountPoint;
+using android::fs_mgr::ReadDefaultFstab;
+using android::vold::KeyBuffer;
+
+static const std::string kDmNameUserdata = "userdata";
+
+static const char* kFn_keymaster_key_blob = "keymaster_key_blob";
+static const char* kFn_keymaster_key_blob_upgraded = "keymaster_key_blob_upgraded";
+
+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(android::vold::sFsckContext)) {
+ 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,
+ false);
+ 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;
+}
+
+android::fs_mgr::Fstab fstab_default;
+
+namespace android {
+namespace vold {
+
+// Note: It is possible to orphan a key if it is removed before deleting
+// Update this once keymaster APIs change, and we have a proper commit.
+static void commit_key(const std::string& dir) {
+ while (!android::base::WaitForProperty("vold.checkpoint_committed", "1")) {
+ LOG(ERROR) << "Wait for boot timed out";
+ }
+ Keymaster keymaster;
+ auto keyPath = dir + "/" + kFn_keymaster_key_blob;
+ auto newKeyPath = dir + "/" + kFn_keymaster_key_blob_upgraded;
+ std::string key;
+
+ if (!android::base::ReadFileToString(keyPath, &key)) {
+ LOG(ERROR) << "Failed to read old key: " << dir;
+ return;
+ }
+ if (rename(newKeyPath.c_str(), keyPath.c_str()) != 0) {
+ PLOG(ERROR) << "Unable to move upgraded key to location: " << keyPath;
+ return;
+ }
+ if (!keymaster.deleteKey(key)) {
+ LOG(ERROR) << "Key deletion failed during upgrade, continuing anyway: " << dir;
+ }
+ LOG(INFO) << "Old Key deleted: " << dir;
+}
+
+static bool read_key(const FstabEntry& data_rec, bool create_if_absent, KeyBuffer* key) {
+ if (data_rec.key_dir.empty()) {
+ LOG(ERROR) << "Failed to get key_dir";
+ return false;
+ }
+ std::string key_dir = data_rec.key_dir;
+ std::string sKey;
+ auto dir = key_dir + "/key";
+ LOG(DEBUG) << "key_dir/key: " << dir;
+ if (fs_mkdirs(dir.c_str(), 0700)) {
+ PLOG(ERROR) << "Creating directories: " << dir;
+ return false;
+ }
+ auto temp = key_dir + "/tmp";
+ auto newKeyPath = dir + "/" + kFn_keymaster_key_blob_upgraded;
+ /* If we have a leftover upgraded key, delete it.
+ * We either failed an update and must return to the old key,
+ * or we rebooted before commiting the keys in a freak accident.
+ * Either way, we can re-upgrade the key if we need to.
+ */
+ Keymaster keymaster;
+ if (pathExists(newKeyPath)) {
+ if (!android::base::ReadFileToString(newKeyPath, &sKey))
+ LOG(ERROR) << "Failed to read old key: " << dir;
+ else if (!keymaster.deleteKey(sKey))
+ LOG(ERROR) << "Old key deletion failed, continuing anyway: " << dir;
+ else
+ unlink(newKeyPath.c_str());
+ }
+ // bool needs_cp = cp_needsCheckpoint();
+ bool needs_cp = false;
+ if (!android::vold::retrieveKey(create_if_absent, dir, temp, key, needs_cp)) return false;
+ if (needs_cp && pathExists(newKeyPath)) std::thread(commit_key, dir).detach();
+ return true;
+}
+
+} // namespace vold
+} // namespace android
+
+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";
+ 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) {
+ if (android::vold::GetBlockDev512Sectors(real_blkdev, nr_sec) != android::OK) {
+ PLOG(ERROR) << "Unable to measure size of " << real_blkdev;
+ return false;
+ }
+ 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";
+ 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) {
+ 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";
+ 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;
+ 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;
+ 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";
+ 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";
+ return false;
+ }
+ PLOG(INFO) << "DM_TABLE_LOAD ioctl failed, retrying";
+ 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;
+ return false;
+ }
+ return true;
+}
+
+bool fscrypt_mount_metadata_encrypted(const std::string& blk_device, const std::string& mount_point,
+ bool needs_encrypt) {
+ LOG(ERROR) << "fscrypt_mount_metadata_encrypted: " << blk_device << " " << mount_point << " " << needs_encrypt;
+ // auto encrypted_state = android::base::GetProperty("ro.crypto.state", "");
+ // if (encrypted_state != "") {
+ // LOG(ERROR) << "fscrypt_enable_crypto got unexpected starting state: " << encrypted_state;
+ // return false;
+ // }
+
+ if (!ReadDefaultFstab(&fstab_default)) {
+ PLOG(ERROR) << "Failed to open default fstab";
+ return -1;
+ }
+
+ auto data_rec = GetEntryForMountPoint(&fstab_default, mount_point);
+ if (!data_rec) {
+ LOG(ERROR) << "Failed to get data_rec";
+ return false;
+ }
+ KeyBuffer key;
+ if (!read_key(*data_rec, needs_encrypt, &key)) return false;
+ uint64_t nr_sec;
+ if (!get_number_of_sectors(data_rec->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(crypto_blkdev.data(), blk_device.data(), nr_sec,
+ &size_already_done, nr_sec, 0, false);
+ if (rc != 0) {
+ LOG(ERROR) << "Inplace crypto failed with code: " << rc;
+ return false;
+ }
+ if (static_cast<uint64_t>(size_already_done) != nr_sec) {
+ LOG(ERROR) << "Inplace crypto only got up to sector: " << size_already_done;
+ return false;
+ }
+ LOG(INFO) << "Inplace encryption complete";
+ }
+
+ LOG(ERROR) << "Mounting metadata-encrypted filesystem:" << mount_point;
+ mount_via_fs_mgr(data_rec->mount_point.c_str(), crypto_blkdev.c_str());
+ android::base::SetProperty("ro.crypto.fs_crypto_blkdev", crypto_blkdev);
+ return true;
+}
diff --git a/crypto/fscrypt/MetadataCrypt.h b/crypto/fscrypt/MetadataCrypt.h
new file mode 100644
index 0000000..cd0f5e5
--- /dev/null
+++ b/crypto/fscrypt/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>
+
+bool fscrypt_mount_metadata_encrypted(const std::string& block_device,
+ const std::string& mount_point, bool needs_encrypt);
+
+#endif
diff --git a/crypto/fscrypt/Process.cpp b/crypto/fscrypt/Process.cpp
new file mode 100644
index 0000000..3d8e3d7
--- /dev/null
+++ b/crypto/fscrypt/Process.cpp
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <fts.h>
+#include <poll.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <fstream>
+#include <unordered_set>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
+#include "Process.h"
+
+using android::base::StringPrintf;
+
+namespace android {
+namespace vold {
+
+static bool checkMaps(const std::string& path, const std::string& prefix) {
+ bool found = false;
+ auto file = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path.c_str(), "re"), fclose};
+ if (!file) {
+ return false;
+ }
+
+ char* buf = nullptr;
+ size_t len = 0;
+ while (getline(&buf, &len, file.get()) != -1) {
+ std::string line(buf);
+ std::string::size_type pos = line.find('/');
+ if (pos != std::string::npos) {
+ line = line.substr(pos);
+ if (android::base::StartsWith(line, prefix)) {
+ LOG(WARNING) << "Found map " << path << " referencing " << line;
+ found = true;
+ break;
+ }
+ }
+ }
+ free(buf);
+
+ return found;
+}
+
+static bool checkSymlink(const std::string& path, const std::string& prefix) {
+ std::string res;
+ if (android::base::Readlink(path, &res)) {
+ if (android::base::StartsWith(res, prefix)) {
+ LOG(WARNING) << "Found symlink " << path << " referencing " << res;
+ return true;
+ }
+ }
+ return false;
+}
+
+int KillProcessesWithOpenFiles(const std::string& prefix, int signal) {
+ std::unordered_set<pid_t> pids;
+
+ auto proc_d = std::unique_ptr<DIR, int (*)(DIR*)>(opendir("/proc"), closedir);
+ if (!proc_d) {
+ PLOG(ERROR) << "Failed to open proc";
+ return -1;
+ }
+
+ struct dirent* proc_de;
+ while ((proc_de = readdir(proc_d.get())) != nullptr) {
+ // We only care about valid PIDs
+ pid_t pid;
+ if (proc_de->d_type != DT_DIR) continue;
+ if (!android::base::ParseInt(proc_de->d_name, &pid)) continue;
+
+ // Look for references to prefix
+ bool found = false;
+ auto path = StringPrintf("/proc/%d", pid);
+ found |= checkMaps(path + "/maps", prefix);
+ found |= checkSymlink(path + "/cwd", prefix);
+ found |= checkSymlink(path + "/root", prefix);
+ found |= checkSymlink(path + "/exe", prefix);
+
+ auto fd_path = path + "/fd";
+ auto fd_d = std::unique_ptr<DIR, int (*)(DIR*)>(opendir(fd_path.c_str()), closedir);
+ if (!fd_d) {
+ PLOG(WARNING) << "Failed to open " << fd_path;
+ } else {
+ struct dirent* fd_de;
+ while ((fd_de = readdir(fd_d.get())) != nullptr) {
+ if (fd_de->d_type != DT_LNK) continue;
+ found |= checkSymlink(fd_path + "/" + fd_de->d_name, prefix);
+ }
+ }
+
+ if (found) {
+ pids.insert(pid);
+ }
+ }
+ if (signal != 0) {
+ for (const auto& pid : pids) {
+ LOG(WARNING) << "Sending " << strsignal(signal) << " to " << pid;
+ kill(pid, signal);
+ }
+ }
+ return pids.size();
+}
+
+} // namespace vold
+} // namespace android
diff --git a/crypto/fscrypt/Process.h b/crypto/fscrypt/Process.h
new file mode 100644
index 0000000..1406782
--- /dev/null
+++ b/crypto/fscrypt/Process.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _PROCESS_H
+#define _PROCESS_H
+
+namespace android {
+namespace vold {
+
+int KillProcessesWithOpenFiles(const std::string& path, int signal);
+
+} // namespace vold
+} // namespace android
+
+#endif
diff --git a/crypto/fscrypt/ScryptParameters.cpp b/crypto/fscrypt/ScryptParameters.cpp
new file mode 100644
index 0000000..669809b
--- /dev/null
+++ b/crypto/fscrypt/ScryptParameters.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ScryptParameters.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+bool parse_scrypt_parameters(const char* paramstr, int *Nf, int *rf, int *pf) {
+ int params[3];
+ char *token;
+ char *saveptr;
+ int i;
+
+ /*
+ * The token we're looking for should be three integers separated by
+ * colons (e.g., "12:8:1"). Scan the property to make sure it matches.
+ */
+ for (i = 0, token = strtok_r(const_cast<char *>(paramstr), ":", &saveptr);
+ token != nullptr && i < 3;
+ i++, token = strtok_r(nullptr, ":", &saveptr)) {
+ char *endptr;
+ params[i] = strtol(token, &endptr, 10);
+
+ /*
+ * Check that there was a valid number and it's 8-bit.
+ */
+ if ((*token == '\0') || (*endptr != '\0') || params[i] < 0 || params[i] > 255) {
+ return false;
+ }
+ }
+ if (token != nullptr) {
+ return false;
+ }
+ *Nf = params[0]; *rf = params[1]; *pf = params[2];
+ return true;
+}
diff --git a/crypto/fscrypt/ScryptParameters.h b/crypto/fscrypt/ScryptParameters.h
new file mode 100644
index 0000000..1b43ea5
--- /dev/null
+++ b/crypto/fscrypt/ScryptParameters.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_VOLD_SCRYPT_PARAMETERS_H
+#define ANDROID_VOLD_SCRYPT_PARAMETERS_H
+
+#include <stdbool.h>
+#include <sys/cdefs.h>
+
+#define SCRYPT_PROP "ro.crypto.scrypt_params"
+#define SCRYPT_DEFAULTS "15:3:1"
+
+__BEGIN_DECLS
+
+bool parse_scrypt_parameters(const char* paramstr, int *Nf, int *rf, int *pf);
+
+__END_DECLS
+
+#endif
diff --git a/crypto/fscrypt/Utils.cpp b/crypto/fscrypt/Utils.cpp
new file mode 100755
index 0000000..aa71d8f
--- /dev/null
+++ b/crypto/fscrypt/Utils.cpp
@@ -0,0 +1,989 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Utils.h"
+
+#include "Process.h"
+#include "sehandle.h"
+
+#include <android-base/chrono_utils.h>
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <cutils/fs.h>
+#include <logwrap/logwrap.h>
+#include <private/android_filesystem_config.h>
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <linux/fs.h>
+#include <mntent.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <sys/sysmacros.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <list>
+#include <mutex>
+#include <thread>
+
+#ifndef UMOUNT_NOFOLLOW
+#define UMOUNT_NOFOLLOW 0x00000008 /* Don't follow symlink on umount */
+#endif
+
+using namespace std::chrono_literals;
+using android::base::ReadFileToString;
+using android::base::StringPrintf;
+
+namespace android {
+namespace vold {
+
+security_context_t sBlkidContext = nullptr;
+security_context_t sBlkidUntrustedContext = nullptr;
+security_context_t sFsckContext = nullptr;
+security_context_t sFsckUntrustedContext = nullptr;
+struct selabel_handle* sehandle;
+
+bool sSleepOnUnmount = true;
+
+static const char* kBlkidPath = "/system/bin/blkid";
+static const char* kKeyPath = "/data/misc/vold";
+
+static const char* kProcFilesystems = "/proc/filesystems";
+
+// Lock used to protect process-level SELinux changes from racing with each
+// other between multiple threads.
+static std::mutex kSecurityLock;
+
+status_t CreateDeviceNode(const std::string& path, dev_t dev) {
+ std::lock_guard<std::mutex> lock(kSecurityLock);
+ const char* cpath = path.c_str();
+ status_t res = 0;
+
+ char* secontext = nullptr;
+ if (sehandle) {
+ if (!selabel_lookup(sehandle, &secontext, cpath, S_IFBLK)) {
+ setfscreatecon(secontext);
+ }
+ }
+
+ mode_t mode = 0660 | S_IFBLK;
+ if (mknod(cpath, mode, dev) < 0) {
+ if (errno != EEXIST) {
+ PLOG(ERROR) << "Failed to create device node for " << major(dev) << ":" << minor(dev)
+ << " at " << path;
+ res = -errno;
+ }
+ }
+
+ if (secontext) {
+ setfscreatecon(nullptr);
+ freecon(secontext);
+ }
+
+ return res;
+}
+
+status_t DestroyDeviceNode(const std::string& path) {
+ const char* cpath = path.c_str();
+ if (TEMP_FAILURE_RETRY(unlink(cpath))) {
+ return -errno;
+ } else {
+ return OK;
+ }
+}
+
+status_t PrepareDir(const std::string& path, mode_t mode, uid_t uid, gid_t gid) {
+ std::lock_guard<std::mutex> lock(kSecurityLock);
+ const char* cpath = path.c_str();
+
+ char* secontext = nullptr;
+ if (sehandle) {
+ if (!selabel_lookup(sehandle, &secontext, cpath, S_IFDIR)) {
+ setfscreatecon(secontext);
+ }
+ }
+
+ int res = fs_prepare_dir(cpath, mode, uid, gid);
+
+ if (secontext) {
+ setfscreatecon(nullptr);
+ freecon(secontext);
+ }
+
+ if (res == 0) {
+ return OK;
+ } else {
+ return -errno;
+ }
+}
+
+status_t ForceUnmount(const std::string& path) {
+ const char* cpath = path.c_str();
+ if (!umount2(cpath, UMOUNT_NOFOLLOW) || errno == EINVAL || errno == ENOENT) {
+ return OK;
+ }
+ // Apps might still be handling eject request, so wait before
+ // we start sending signals
+ if (sSleepOnUnmount) sleep(5);
+
+ KillProcessesWithOpenFiles(path, SIGINT);
+ if (sSleepOnUnmount) sleep(5);
+ if (!umount2(cpath, UMOUNT_NOFOLLOW) || errno == EINVAL || errno == ENOENT) {
+ return OK;
+ }
+
+ KillProcessesWithOpenFiles(path, SIGTERM);
+ if (sSleepOnUnmount) sleep(5);
+ if (!umount2(cpath, UMOUNT_NOFOLLOW) || errno == EINVAL || errno == ENOENT) {
+ return OK;
+ }
+
+ KillProcessesWithOpenFiles(path, SIGKILL);
+ if (sSleepOnUnmount) sleep(5);
+ if (!umount2(cpath, UMOUNT_NOFOLLOW) || errno == EINVAL || errno == ENOENT) {
+ return OK;
+ }
+
+ return -errno;
+}
+
+status_t KillProcessesUsingPath(const std::string& path) {
+ if (KillProcessesWithOpenFiles(path, SIGINT) == 0) {
+ return OK;
+ }
+ if (sSleepOnUnmount) sleep(5);
+
+ if (KillProcessesWithOpenFiles(path, SIGTERM) == 0) {
+ return OK;
+ }
+ if (sSleepOnUnmount) sleep(5);
+
+ if (KillProcessesWithOpenFiles(path, SIGKILL) == 0) {
+ return OK;
+ }
+ if (sSleepOnUnmount) sleep(5);
+
+ // Send SIGKILL a second time to determine if we've
+ // actually killed everyone with open files
+ if (KillProcessesWithOpenFiles(path, SIGKILL) == 0) {
+ return OK;
+ }
+ PLOG(ERROR) << "Failed to kill processes using " << path;
+ return -EBUSY;
+}
+
+status_t BindMount(const std::string& source, const std::string& target) {
+ if (UnmountTree(target) < 0) {
+ return -errno;
+ }
+ if (TEMP_FAILURE_RETRY(mount(source.c_str(), target.c_str(), nullptr, MS_BIND, nullptr)) < 0) {
+ PLOG(ERROR) << "Failed to bind mount " << source << " to " << target;
+ return -errno;
+ }
+ return OK;
+}
+
+status_t Symlink(const std::string& target, const std::string& linkpath) {
+ if (Unlink(linkpath) < 0) {
+ return -errno;
+ }
+ if (TEMP_FAILURE_RETRY(symlink(target.c_str(), linkpath.c_str())) < 0) {
+ PLOG(ERROR) << "Failed to create symlink " << linkpath << " to " << target;
+ return -errno;
+ }
+ return OK;
+}
+
+status_t Unlink(const std::string& linkpath) {
+ if (TEMP_FAILURE_RETRY(unlink(linkpath.c_str())) < 0 && errno != EINVAL && errno != ENOENT) {
+ PLOG(ERROR) << "Failed to unlink " << linkpath;
+ return -errno;
+ }
+ return OK;
+}
+
+status_t CreateDir(const std::string& dir, mode_t mode) {
+ struct stat sb;
+ if (TEMP_FAILURE_RETRY(stat(dir.c_str(), &sb)) == 0) {
+ if (S_ISDIR(sb.st_mode)) {
+ return OK;
+ } else if (TEMP_FAILURE_RETRY(unlink(dir.c_str())) == -1) {
+ PLOG(ERROR) << "Failed to unlink " << dir;
+ return -errno;
+ }
+ } else if (errno != ENOENT) {
+ PLOG(ERROR) << "Failed to stat " << dir;
+ return -errno;
+ }
+ if (TEMP_FAILURE_RETRY(mkdir(dir.c_str(), mode)) == -1 && errno != EEXIST) {
+ PLOG(ERROR) << "Failed to mkdir " << dir;
+ return -errno;
+ }
+ return OK;
+}
+
+bool FindValue(const std::string& raw, const std::string& key, std::string* value) {
+ auto qual = key + "=\"";
+ size_t start = 0;
+ while (true) {
+ start = raw.find(qual, start);
+ if (start == std::string::npos) return false;
+ if (start == 0 || raw[start - 1] == ' ') {
+ break;
+ }
+ start += 1;
+ }
+ start += qual.length();
+
+ auto end = raw.find("\"", start);
+ if (end == std::string::npos) return false;
+
+ *value = raw.substr(start, end - start);
+ return true;
+}
+
+static status_t readMetadata(const std::string& path, std::string* fsType, std::string* fsUuid,
+ std::string* fsLabel, bool untrusted) {
+ fsType->clear();
+ fsUuid->clear();
+ fsLabel->clear();
+
+ std::vector<std::string> cmd;
+ cmd.push_back(kBlkidPath);
+ cmd.push_back("-c");
+ cmd.push_back("/dev/null");
+ cmd.push_back("-s");
+ cmd.push_back("TYPE");
+ cmd.push_back("-s");
+ cmd.push_back("UUID");
+ cmd.push_back("-s");
+ cmd.push_back("LABEL");
+ cmd.push_back(path);
+
+ std::vector<std::string> output;
+ status_t res = ForkExecvp(cmd, &output, untrusted ? sBlkidUntrustedContext : sBlkidContext);
+ if (res != OK) {
+ LOG(WARNING) << "blkid failed to identify " << path;
+ return res;
+ }
+
+ for (const auto& line : output) {
+ // Extract values from blkid output, if defined
+ FindValue(line, "TYPE", fsType);
+ FindValue(line, "UUID", fsUuid);
+ FindValue(line, "LABEL", fsLabel);
+ }
+
+ return OK;
+}
+
+status_t ReadMetadata(const std::string& path, std::string* fsType, std::string* fsUuid,
+ std::string* fsLabel) {
+ return readMetadata(path, fsType, fsUuid, fsLabel, false);
+}
+
+status_t ReadMetadataUntrusted(const std::string& path, std::string* fsType, std::string* fsUuid,
+ std::string* fsLabel) {
+ return readMetadata(path, fsType, fsUuid, fsLabel, true);
+}
+
+static std::vector<const char*> ConvertToArgv(const std::vector<std::string>& args) {
+ std::vector<const char*> argv;
+ argv.reserve(args.size() + 1);
+ for (const auto& arg : args) {
+ if (argv.empty()) {
+ LOG(DEBUG) << arg;
+ } else {
+ LOG(DEBUG) << " " << arg;
+ }
+ argv.emplace_back(arg.data());
+ }
+ argv.emplace_back(nullptr);
+ return argv;
+}
+
+static status_t ReadLinesFromFdAndLog(std::vector<std::string>* output,
+ android::base::unique_fd ufd) {
+ std::unique_ptr<FILE, int (*)(FILE*)> fp(android::base::Fdopen(std::move(ufd), "r"), fclose);
+ if (!fp) {
+ PLOG(ERROR) << "fdopen in ReadLinesFromFdAndLog";
+ return -errno;
+ }
+ if (output) output->clear();
+ char line[1024];
+ while (fgets(line, sizeof(line), fp.get()) != nullptr) {
+ LOG(DEBUG) << line;
+ if (output) output->emplace_back(line);
+ }
+ return OK;
+}
+
+status_t ForkExecvp(const std::vector<std::string>& args, std::vector<std::string>* output,
+ security_context_t context) {
+ auto argv = ConvertToArgv(args);
+
+ android::base::unique_fd pipe_read, pipe_write;
+ if (!android::base::Pipe(&pipe_read, &pipe_write)) {
+ PLOG(ERROR) << "Pipe in ForkExecvp";
+ return -errno;
+ }
+
+ pid_t pid = fork();
+ if (pid == 0) {
+ if (context) {
+ if (setexeccon(context)) {
+ LOG(ERROR) << "Failed to setexeccon in ForkExecvp";
+ abort();
+ }
+ }
+ pipe_read.reset();
+ if (dup2(pipe_write.get(), STDOUT_FILENO) == -1) {
+ PLOG(ERROR) << "dup2 in ForkExecvp";
+ _exit(EXIT_FAILURE);
+ }
+ pipe_write.reset();
+ execvp(argv[0], const_cast<char**>(argv.data()));
+ PLOG(ERROR) << "exec in ForkExecvp" << " cmd: " << argv[0];
+ _exit(EXIT_FAILURE);
+ }
+ if (pid == -1) {
+ PLOG(ERROR) << "fork in ForkExecvp";
+ return -errno;
+ }
+
+ pipe_write.reset();
+ auto st = ReadLinesFromFdAndLog(output, std::move(pipe_read));
+ if (st != 0) return st;
+
+ int status;
+ if (waitpid(pid, &status, 0) == -1) {
+ PLOG(ERROR) << "waitpid in ForkExecvp";
+ return -errno;
+ }
+ if (!WIFEXITED(status)) {
+ LOG(ERROR) << "Process did not exit normally, status: " << status;
+ return -ECHILD;
+ }
+ if (WEXITSTATUS(status)) {
+ LOG(ERROR) << "Process exited with code: " << WEXITSTATUS(status);
+ return WEXITSTATUS(status);
+ }
+ return OK;
+}
+
+pid_t ForkExecvpAsync(const std::vector<std::string>& args) {
+ auto argv = ConvertToArgv(args);
+
+ pid_t pid = fork();
+ if (pid == 0) {
+ close(STDIN_FILENO);
+ close(STDOUT_FILENO);
+ close(STDERR_FILENO);
+
+ execvp(argv[0], const_cast<char**>(argv.data()));
+ PLOG(ERROR) << "exec in ForkExecvpAsync";
+ _exit(EXIT_FAILURE);
+ }
+ if (pid == -1) {
+ PLOG(ERROR) << "fork in ForkExecvpAsync";
+ return -1;
+ }
+ 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;
+ }
+
+ ssize_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 GenerateRandomUuid(std::string& out) {
+ status_t res = ReadRandomBytes(16, out);
+ if (res == OK) {
+ out[6] &= 0x0f; /* clear version */
+ out[6] |= 0x40; /* set to version 4 */
+ out[8] &= 0x3f; /* clear variant */
+ out[8] |= 0x80; /* set to IETF variant */
+ }
+ return res;
+}
+
+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]) {
+ // clang-format off
+ 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;
+ // clang-format on
+ }
+
+ if (even) {
+ cur = val << 4;
+ } else {
+ cur += val;
+ str.push_back(cur);
+ cur = 0;
+ }
+ even = !even;
+ }
+ return even ? OK : -EINVAL;
+}
+
+static const char* kLookup = "0123456789abcdef";
+
+status_t StrToHex(const std::string& str, std::string& hex) {
+ hex.clear();
+ for (size_t i = 0; i < str.size(); i++) {
+ hex.push_back(kLookup[(str[i] & 0xF0) >> 4]);
+ hex.push_back(kLookup[str[i] & 0x0F]);
+ }
+ return OK;
+}
+
+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 OK;
+}
+
+status_t NormalizeHex(const std::string& in, std::string& out) {
+ std::string tmp;
+ if (HexToStr(in, tmp)) {
+ return -EINVAL;
+ }
+ return StrToHex(tmp, out);
+}
+
+status_t GetBlockDevSize(int fd, uint64_t* size) {
+ if (ioctl(fd, BLKGETSIZE64, size)) {
+ return -errno;
+ }
+
+ return OK;
+}
+
+status_t GetBlockDevSize(const std::string& path, uint64_t* size) {
+ int fd = open(path.c_str(), O_RDONLY | O_CLOEXEC);
+ status_t res = OK;
+
+ if (fd < 0) {
+ return -errno;
+ }
+
+ res = GetBlockDevSize(fd, size);
+
+ close(fd);
+
+ return res;
+}
+
+status_t GetBlockDev512Sectors(const std::string& path, uint64_t* nr_sec) {
+ uint64_t size;
+ status_t res = GetBlockDevSize(path, &size);
+
+ if (res != OK) {
+ return res;
+ }
+
+ *nr_sec = size / 512;
+
+ return OK;
+}
+
+uint64_t GetFreeBytes(const std::string& path) {
+ struct statvfs sb;
+ if (statvfs(path.c_str(), &sb) == 0) {
+ return (uint64_t)sb.f_bavail * sb.f_frsize;
+ } else {
+ return -1;
+ }
+}
+
+// TODO: borrowed from frameworks/native/libs/diskusage/ which should
+// eventually be migrated into system/
+static int64_t stat_size(struct stat* s) {
+ int64_t blksize = s->st_blksize;
+ // count actual blocks used instead of nominal file size
+ int64_t size = s->st_blocks * 512;
+
+ if (blksize) {
+ /* round up to filesystem block size */
+ size = (size + blksize - 1) & (~(blksize - 1));
+ }
+
+ return size;
+}
+
+// TODO: borrowed from frameworks/native/libs/diskusage/ which should
+// eventually be migrated into system/
+int64_t calculate_dir_size(int dfd) {
+ int64_t size = 0;
+ struct stat s;
+ DIR* d;
+ struct dirent* de;
+
+ d = fdopendir(dfd);
+ if (d == NULL) {
+ close(dfd);
+ return 0;
+ }
+
+ while ((de = readdir(d))) {
+ const char* name = de->d_name;
+ if (fstatat(dfd, name, &s, AT_SYMLINK_NOFOLLOW) == 0) {
+ size += stat_size(&s);
+ }
+ if (de->d_type == DT_DIR) {
+ int subfd;
+
+ /* always skip "." and ".." */
+ if (name[0] == '.') {
+ if (name[1] == 0) continue;
+ if ((name[1] == '.') && (name[2] == 0)) continue;
+ }
+
+ subfd = openat(dfd, name, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
+ if (subfd >= 0) {
+ size += calculate_dir_size(subfd);
+ }
+ }
+ }
+ closedir(d);
+ return size;
+}
+
+uint64_t GetTreeBytes(const std::string& path) {
+ int dirfd = open(path.c_str(), O_RDONLY | O_DIRECTORY | O_CLOEXEC);
+ if (dirfd < 0) {
+ PLOG(WARNING) << "Failed to open " << path;
+ return -1;
+ } else {
+ return calculate_dir_size(dirfd);
+ }
+}
+
+bool IsFilesystemSupported(const std::string& fsType) {
+ std::string supported;
+ if (!ReadFileToString(kProcFilesystems, &supported)) {
+ PLOG(ERROR) << "Failed to read supported filesystems";
+ return false;
+ }
+ return supported.find(fsType + "\n") != std::string::npos;
+}
+
+status_t WipeBlockDevice(const std::string& path) {
+ status_t res = -1;
+ const char* c_path = path.c_str();
+ uint64_t range[2] = {0, 0};
+
+ int fd = TEMP_FAILURE_RETRY(open(c_path, O_RDWR | O_CLOEXEC));
+ if (fd == -1) {
+ PLOG(ERROR) << "Failed to open " << path;
+ goto done;
+ }
+
+ if (GetBlockDevSize(fd, &range[1]) != OK) {
+ PLOG(ERROR) << "Failed to determine size of " << path;
+ goto done;
+ }
+
+ LOG(INFO) << "About to discard " << range[1] << " on " << path;
+ if (ioctl(fd, BLKDISCARD, &range) == 0) {
+ LOG(INFO) << "Discard success on " << path;
+ res = 0;
+ } else {
+ PLOG(ERROR) << "Discard failure on " << path;
+ }
+
+done:
+ close(fd);
+ return res;
+}
+
+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("").c_str(), userId);
+}
+
+std::string BuildDataSystemCePath(userid_t userId) {
+ return StringPrintf("%s/system_ce/%u", BuildDataPath("").c_str(), userId);
+}
+
+std::string BuildDataSystemDePath(userid_t userId) {
+ return StringPrintf("%s/system_de/%u", BuildDataPath("").c_str(), userId);
+}
+
+std::string BuildDataMiscLegacyPath(userid_t userId) {
+ return StringPrintf("%s/misc/user/%u", BuildDataPath("").c_str(), userId);
+}
+
+std::string BuildDataMiscCePath(userid_t userId) {
+ return StringPrintf("%s/misc_ce/%u", BuildDataPath("").c_str(), userId);
+}
+
+std::string BuildDataMiscDePath(userid_t userId) {
+ return StringPrintf("%s/misc_de/%u", BuildDataPath("").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("").c_str(), userId);
+}
+
+std::string BuildDataVendorCePath(userid_t userId) {
+ return StringPrintf("%s/vendor_ce/%u", BuildDataPath("").c_str(), userId);
+}
+
+std::string BuildDataVendorDePath(userid_t userId) {
+ return StringPrintf("%s/vendor_de/%u", BuildDataPath("").c_str(), userId);
+}
+
+std::string BuildDataPath(const std::string& volumeUuid) {
+ // TODO: unify with installd path generation logic
+ if (volumeUuid.empty()) {
+ return "/data";
+ } else {
+ CHECK(isValidFilename(volumeUuid));
+ return StringPrintf("/mnt/expand/%s", volumeUuid.c_str());
+ }
+}
+
+std::string BuildDataMediaCePath(const std::string& 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 std::string& volumeUuid, userid_t userId) {
+ // TODO: unify with installd path generation logic
+ std::string data(BuildDataPath(volumeUuid));
+ if (volumeUuid.empty() && userId == 0) {
+ std::string legacy = StringPrintf("%s/data", data.c_str());
+ struct stat sb;
+ if (lstat(legacy.c_str(), &sb) == 0 && S_ISDIR(sb.st_mode)) {
+ /* /data/data is dir, return /data/data for legacy system */
+ return legacy;
+ }
+ }
+ return StringPrintf("%s/user/%u", data.c_str(), userId);
+}
+
+std::string BuildDataUserDePath(const std::string& 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);
+}
+
+dev_t GetDevice(const std::string& path) {
+ struct stat sb;
+ if (stat(path.c_str(), &sb)) {
+ PLOG(WARNING) << "Failed to stat " << path;
+ return 0;
+ } else {
+ return sb.st_dev;
+ }
+}
+
+status_t RestoreconRecursive(const std::string& path) {
+ LOG(DEBUG) << "Starting restorecon of " << path;
+
+ static constexpr const char* kRestoreconString = "selinux.restorecon_recursive";
+
+ android::base::SetProperty(kRestoreconString, "");
+ android::base::SetProperty(kRestoreconString, path);
+
+ android::base::WaitForProperty(kRestoreconString, path);
+
+ LOG(DEBUG) << "Finished restorecon of " << path;
+ return OK;
+}
+
+bool Readlinkat(int dirfd, const std::string& path, std::string* result) {
+ // Shamelessly borrowed from android::base::Readlink()
+ result->clear();
+
+ // Most Linux file systems (ext2 and ext4, say) limit symbolic links to
+ // 4095 bytes. Since we'll copy out into the string anyway, it doesn't
+ // waste memory to just start there. We add 1 so that we can recognize
+ // whether it actually fit (rather than being truncated to 4095).
+ std::vector<char> buf(4095 + 1);
+ while (true) {
+ ssize_t size = readlinkat(dirfd, path.c_str(), &buf[0], buf.size());
+ // Unrecoverable error?
+ if (size == -1) return false;
+ // It fit! (If size == buf.size(), it may have been truncated.)
+ if (static_cast<size_t>(size) < buf.size()) {
+ result->assign(&buf[0], size);
+ return true;
+ }
+ // Double our buffer and try again.
+ buf.resize(buf.size() * 2);
+ }
+}
+
+bool IsRunningInEmulator() {
+ return android::base::GetBoolProperty("ro.kernel.qemu", false);
+}
+
+static status_t findMountPointsWithPrefix(const std::string& prefix,
+ std::list<std::string>& mountPoints) {
+ // Add a trailing slash if the client didn't provide one so that we don't match /foo/barbaz
+ // when the prefix is /foo/bar
+ std::string prefixWithSlash(prefix);
+ if (prefix.back() != '/') {
+ android::base::StringAppendF(&prefixWithSlash, "/");
+ }
+
+ std::unique_ptr<FILE, int (*)(FILE*)> mnts(setmntent("/proc/mounts", "re"), endmntent);
+ if (!mnts) {
+ PLOG(ERROR) << "Unable to open /proc/mounts";
+ return -errno;
+ }
+
+ // Some volumes can be stacked on each other, so force unmount in
+ // reverse order to give us the best chance of success.
+ struct mntent* mnt; // getmntent returns a thread local, so it's safe.
+ while ((mnt = getmntent(mnts.get())) != nullptr) {
+ auto mountPoint = std::string(mnt->mnt_dir) + "/";
+ if (android::base::StartsWith(mountPoint, prefixWithSlash)) {
+ mountPoints.push_front(mountPoint);
+ }
+ }
+ return OK;
+}
+
+// Unmount all mountpoints that start with prefix. prefix itself doesn't need to be a mountpoint.
+status_t UnmountTreeWithPrefix(const std::string& prefix) {
+ std::list<std::string> toUnmount;
+ status_t result = findMountPointsWithPrefix(prefix, toUnmount);
+ if (result < 0) {
+ return result;
+ }
+ for (const auto& path : toUnmount) {
+ if (umount2(path.c_str(), MNT_DETACH)) {
+ PLOG(ERROR) << "Failed to unmount " << path;
+ result = -errno;
+ }
+ }
+ return result;
+}
+
+status_t UnmountTree(const std::string& mountPoint) {
+ if (TEMP_FAILURE_RETRY(umount2(mountPoint.c_str(), MNT_DETACH)) < 0 && errno != EINVAL &&
+ errno != ENOENT) {
+ PLOG(ERROR) << "Failed to unmount " << mountPoint;
+ return -errno;
+ }
+ return OK;
+}
+
+static status_t delete_dir_contents(DIR* dir) {
+ // Shamelessly borrowed from android::installd
+ int dfd = dirfd(dir);
+ if (dfd < 0) {
+ return -errno;
+ }
+
+ status_t result = OK;
+ struct dirent* de;
+ while ((de = readdir(dir))) {
+ const char* name = de->d_name;
+ if (de->d_type == DT_DIR) {
+ /* always skip "." and ".." */
+ if (name[0] == '.') {
+ if (name[1] == 0) continue;
+ if ((name[1] == '.') && (name[2] == 0)) continue;
+ }
+
+ android::base::unique_fd subfd(
+ openat(dfd, name, O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC));
+ if (subfd.get() == -1) {
+ PLOG(ERROR) << "Couldn't openat " << name;
+ result = -errno;
+ continue;
+ }
+ std::unique_ptr<DIR, decltype(&closedir)> subdirp(
+ android::base::Fdopendir(std::move(subfd)), closedir);
+ if (!subdirp) {
+ PLOG(ERROR) << "Couldn't fdopendir " << name;
+ result = -errno;
+ continue;
+ }
+ result = delete_dir_contents(subdirp.get());
+ if (unlinkat(dfd, name, AT_REMOVEDIR) < 0) {
+ PLOG(ERROR) << "Couldn't unlinkat " << name;
+ result = -errno;
+ }
+ } else {
+ if (unlinkat(dfd, name, 0) < 0) {
+ PLOG(ERROR) << "Couldn't unlinkat " << name;
+ result = -errno;
+ }
+ }
+ }
+ return result;
+}
+
+status_t DeleteDirContentsAndDir(const std::string& pathname) {
+ status_t res = DeleteDirContents(pathname);
+ if (res < 0) {
+ return res;
+ }
+ if (TEMP_FAILURE_RETRY(rmdir(pathname.c_str())) < 0 && errno != ENOENT) {
+ PLOG(ERROR) << "rmdir failed on " << pathname;
+ return -errno;
+ }
+ LOG(VERBOSE) << "Success: rmdir on " << pathname;
+ return OK;
+}
+
+status_t DeleteDirContents(const std::string& pathname) {
+ // Shamelessly borrowed from android::installd
+ std::unique_ptr<DIR, decltype(&closedir)> dirp(opendir(pathname.c_str()), closedir);
+ if (!dirp) {
+ if (errno == ENOENT) {
+ return OK;
+ }
+ PLOG(ERROR) << "Failed to opendir " << pathname;
+ return -errno;
+ }
+ return delete_dir_contents(dirp.get());
+}
+
+// TODO(118708649): fix duplication with init/util.h
+status_t WaitForFile(const char* filename, std::chrono::nanoseconds timeout) {
+ android::base::Timer t;
+ while (t.duration() < timeout) {
+ struct stat sb;
+ if (stat(filename, &sb) != -1) {
+ LOG(INFO) << "wait for '" << filename << "' took " << t;
+ return 0;
+ }
+ std::this_thread::sleep_for(10ms);
+ }
+ LOG(WARNING) << "wait for '" << filename << "' timed out and took " << t;
+ return -1;
+}
+
+bool FsyncDirectory(const std::string& dirname) {
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(dirname.c_str(), O_RDONLY | O_CLOEXEC)));
+ if (fd == -1) {
+ PLOG(ERROR) << "Failed to open " << dirname;
+ return false;
+ }
+ if (fsync(fd) == -1) {
+ if (errno == EROFS || errno == EINVAL) {
+ PLOG(WARNING) << "Skip fsync " << dirname
+ << " on a file system does not support synchronization";
+ } else {
+ PLOG(ERROR) << "Failed to fsync " << dirname;
+ return false;
+ }
+ }
+ return true;
+}
+
+bool writeStringToFile(const std::string& payload, const std::string& filename) {
+ 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;
+}
+
+} // namespace vold
+} // namespace android
diff --git a/crypto/fscrypt/Utils.h b/crypto/fscrypt/Utils.h
new file mode 100755
index 0000000..af4e401
--- /dev/null
+++ b/crypto/fscrypt/Utils.h
@@ -0,0 +1,153 @@
+/*
+ * 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 ANDROID_VOLD_UTILS_H
+#define ANDROID_VOLD_UTILS_H
+
+#include "KeyBuffer.h"
+
+#include <android-base/macros.h>
+#include <cutils/multiuser.h>
+#include <selinux/selinux.h>
+#include <utils/Errors.h>
+
+#include <chrono>
+#include <string>
+#include <vector>
+
+struct DIR;
+
+namespace android {
+namespace vold {
+
+/* SELinux contexts used depending on the block device type */
+extern security_context_t sBlkidContext;
+extern security_context_t sBlkidUntrustedContext;
+extern security_context_t sFsckContext;
+extern security_context_t sFsckUntrustedContext;
+
+// TODO remove this with better solution, b/64143519
+extern bool sSleepOnUnmount;
+
+status_t CreateDeviceNode(const std::string& path, dev_t dev);
+status_t DestroyDeviceNode(const std::string& path);
+
+/* fs_prepare_dir wrapper that creates with SELinux context */
+status_t PrepareDir(const std::string& path, mode_t mode, uid_t uid, gid_t gid);
+
+/* Really unmounts the path, killing active processes along the way */
+status_t ForceUnmount(const std::string& path);
+
+/* Kills any processes using given path */
+status_t KillProcessesUsingPath(const std::string& path);
+
+/* Creates bind mount from source to target */
+status_t BindMount(const std::string& source, const std::string& target);
+
+/** Creates a symbolic link to target */
+status_t Symlink(const std::string& target, const std::string& linkpath);
+
+/** Calls unlink(2) at linkpath */
+status_t Unlink(const std::string& linkpath);
+
+/** Creates the given directory if it is not already available */
+status_t CreateDir(const std::string& dir, mode_t mode);
+
+bool FindValue(const std::string& raw, const std::string& key, std::string* value);
+
+/* Reads filesystem metadata from device at path */
+status_t ReadMetadata(const std::string& path, std::string* fsType, std::string* fsUuid,
+ std::string* fsLabel);
+
+/* Reads filesystem metadata from untrusted device at path */
+status_t ReadMetadataUntrusted(const std::string& path, std::string* fsType, std::string* fsUuid,
+ std::string* fsLabel);
+
+/* Returns either WEXITSTATUS() status, or a negative errno */
+status_t ForkExecvp(const std::vector<std::string>& args, std::vector<std::string>* output = nullptr,
+ security_context_t context = nullptr);
+
+pid_t ForkExecvpAsync(const std::vector<std::string>& args);
+
+/* Gets block device size in bytes */
+status_t GetBlockDevSize(int fd, uint64_t* size);
+status_t GetBlockDevSize(const std::string& path, uint64_t* size);
+/* Gets block device size in 512 byte sectors */
+status_t GetBlockDev512Sectors(const std::string& path, uint64_t* nr_sec);
+
+status_t ReadRandomBytes(size_t bytes, std::string& out);
+status_t ReadRandomBytes(size_t bytes, char* buffer);
+status_t GenerateRandomUuid(std::string& out);
+
+/* Converts hex string to raw bytes, ignoring [ :-] */
+status_t HexToStr(const std::string& hex, std::string& str);
+/* Converts raw bytes to hex string */
+status_t StrToHex(const std::string& str, std::string& hex);
+/* Converts raw key bytes to hex string */
+status_t StrToHex(const KeyBuffer& str, KeyBuffer& hex);
+/* Normalize given hex string into consistent format */
+status_t NormalizeHex(const std::string& in, std::string& out);
+
+uint64_t GetFreeBytes(const std::string& path);
+uint64_t GetTreeBytes(const std::string& path);
+
+bool IsFilesystemSupported(const std::string& fsType);
+
+/* Wipes contents of block device at given path */
+status_t WipeBlockDevice(const std::string& path);
+
+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 BuildDataVendorCePath(userid_t userid);
+std::string BuildDataVendorDePath(userid_t userid);
+
+std::string BuildDataPath(const std::string& volumeUuid);
+std::string BuildDataMediaCePath(const std::string& volumeUuid, userid_t userid);
+std::string BuildDataUserCePath(const std::string& volumeUuid, userid_t userid);
+std::string BuildDataUserDePath(const std::string& volumeUuid, userid_t userid);
+
+dev_t GetDevice(const std::string& path);
+
+status_t RestoreconRecursive(const std::string& path);
+
+// TODO: promote to android::base
+bool Readlinkat(int dirfd, const std::string& path, std::string* result);
+
+/* Checks if Android is running in QEMU */
+bool IsRunningInEmulator();
+
+status_t UnmountTreeWithPrefix(const std::string& prefix);
+status_t UnmountTree(const std::string& mountPoint);
+
+status_t DeleteDirContentsAndDir(const std::string& pathname);
+status_t DeleteDirContents(const std::string& pathname);
+
+status_t WaitForFile(const char* filename, std::chrono::nanoseconds timeout);
+
+bool FsyncDirectory(const std::string& dirname);
+
+bool writeStringToFile(const std::string& payload, const std::string& filename);
+} // namespace vold
+} // namespace android
+
+#endif
diff --git a/crypto/fscrypt/VoldUtil.cpp b/crypto/fscrypt/VoldUtil.cpp
new file mode 100644
index 0000000..082f743
--- /dev/null
+++ b/crypto/fscrypt/VoldUtil.cpp
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2013 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 "VoldUtil.h"
+
+android::fs_mgr::Fstab fstab_default;
diff --git a/crypto/fscrypt/VoldUtil.h b/crypto/fscrypt/VoldUtil.h
new file mode 100644
index 0000000..173c598
--- /dev/null
+++ b/crypto/fscrypt/VoldUtil.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <fstab/fstab.h>
+#include <sys/cdefs.h>
+
+extern android::fs_mgr::Fstab fstab_default;
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
diff --git a/crypto/fscrypt/Weaver1.cpp b/crypto/fscrypt/Weaver1.cpp
new file mode 100644
index 0000000..ea357ed
--- /dev/null
+++ b/crypto/fscrypt/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/fscrypt/Weaver1.h b/crypto/fscrypt/Weaver1.h
new file mode 100644
index 0000000..22f401e
--- /dev/null
+++ b/crypto/fscrypt/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/fscrypt/cryptfs.h b/crypto/fscrypt/cryptfs.h
new file mode 100644
index 0000000..692d7ee
--- /dev/null
+++ b/crypto/fscrypt/cryptfs.h
@@ -0,0 +1,261 @@
+/*
+ * 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 <linux/types.h>
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <cutils/properties.h>
+
+/* The current cryptfs version */
+#define CURRENT_MAJOR_VERSION 1
+#define CURRENT_MINOR_VERSION 3
+
+#define CRYPT_FOOTER_OFFSET 0x4000
+#define CRYPT_FOOTER_TO_PERSIST_OFFSET 0x1000
+#define CRYPT_PERSIST_DATA_SIZE 0x1000
+
+#define MAX_CRYPTO_TYPE_NAME_LEN 64
+
+#define MAX_KEY_LEN 48
+#define SALT_LEN 16
+#define SCRYPT_LEN 32
+
+/* definitions of flags in the structure below */
+#define CRYPT_MNT_KEY_UNENCRYPTED 0x1 /* The key for the partition is not encrypted. */
+#define CRYPT_ENCRYPTION_IN_PROGRESS \
+ 0x2 /* Encryption partially completed, \
+ encrypted_upto valid*/
+#define CRYPT_INCONSISTENT_STATE \
+ 0x4 /* Set when starting encryption, clear when \
+ exit cleanly, either through success or \
+ correctly marked partial encryption */
+#define CRYPT_DATA_CORRUPT \
+ 0x8 /* Set when encryption is fine, but the \
+ underlying volume is corrupt */
+#define CRYPT_FORCE_ENCRYPTION \
+ 0x10 /* Set when it is time to encrypt this \
+ volume on boot. Everything in this \
+ structure is set up correctly as \
+ though device is encrypted except \
+ that the master key is encrypted with the \
+ default password. */
+#define CRYPT_FORCE_COMPLETE \
+ 0x20 /* Set when the above encryption cycle is \
+ complete. On next cryptkeeper entry, match \
+ the password. If it matches fix the master \
+ key and remove this flag. */
+
+/* Allowed values for type in the structure below */
+#define CRYPT_TYPE_PASSWORD \
+ 0 /* master_key is encrypted with a password \
+ * Must be zero to be compatible with pre-L \
+ * devices where type is always password.*/
+#define CRYPT_TYPE_DEFAULT \
+ 1 /* master_key is encrypted with default \
+ * password */
+#define CRYPT_TYPE_PATTERN 2 /* master_key is encrypted with a pattern */
+#define CRYPT_TYPE_PIN 3 /* master_key is encrypted with a pin */
+#define CRYPT_TYPE_MAX_TYPE 3 /* type cannot be larger than this value */
+
+#define CRYPT_MNT_MAGIC 0xD0B5B1C4
+#define PERSIST_DATA_MAGIC 0xE950CD44
+
+/* Key Derivation Function algorithms */
+#define KDF_PBKDF2 1
+#define KDF_SCRYPT 2
+/* Algorithms 3 & 4 deprecated before shipping outside of google, so removed */
+#define KDF_SCRYPT_KEYMASTER 5
+
+/* Maximum allowed keymaster blob size. */
+#define KEYMASTER_BLOB_SIZE 2048
+
+/* __le32 and __le16 defined in system/extras/ext4_utils/ext4_utils.h */
+#define __le8 unsigned char
+
+#if !defined(SHA256_DIGEST_LENGTH)
+#define SHA256_DIGEST_LENGTH 32
+#endif
+
+struct crypt_mnt_ftr {
+ __le32 magic; /* See above */
+ __le16 major_version;
+ __le16 minor_version;
+ __le32 ftr_size; /* in bytes, not including key following */
+ __le32 flags; /* See above */
+ __le32 keysize; /* in bytes */
+ __le32 crypt_type; /* how master_key is encrypted. Must be a
+ * CRYPT_TYPE_XXX value */
+ __le64 fs_size; /* Size of the encrypted fs, in 512 byte sectors */
+ __le32 failed_decrypt_count; /* count of # of failed attempts to decrypt and
+ mount, set to 0 on successful mount */
+ unsigned char crypto_type_name[MAX_CRYPTO_TYPE_NAME_LEN]; /* The type of encryption
+ needed to decrypt this
+ partition, null terminated */
+ __le32 spare2; /* ignored */
+ unsigned char master_key[MAX_KEY_LEN]; /* The encrypted key for decrypting the filesystem */
+ unsigned char salt[SALT_LEN]; /* The salt used for this encryption */
+ __le64 persist_data_offset[2]; /* Absolute offset to both copies of crypt_persist_data
+ * on device with that info, either the footer of the
+ * real_blkdevice or the metadata partition. */
+
+ __le32 persist_data_size; /* The number of bytes allocated to each copy of the
+ * persistent data table*/
+
+ __le8 kdf_type; /* The key derivation function used. */
+
+ /* scrypt parameters. See www.tarsnap.com/scrypt/scrypt.pdf */
+ __le8 N_factor; /* (1 << N) */
+ __le8 r_factor; /* (1 << r) */
+ __le8 p_factor; /* (1 << p) */
+ __le64 encrypted_upto; /* If we are in state CRYPT_ENCRYPTION_IN_PROGRESS and
+ we have to stop (e.g. power low) this is the last
+ encrypted 512 byte sector.*/
+ __le8 hash_first_block[SHA256_DIGEST_LENGTH]; /* When CRYPT_ENCRYPTION_IN_PROGRESS
+ set, hash of first block, used
+ to validate before continuing*/
+
+ /* key_master key, used to sign the derived key which is then used to generate
+ * the intermediate key
+ * This key should be used for no other purposes! We use this key to sign unpadded
+ * data, which is acceptable but only if the key is not reused elsewhere. */
+ __le8 keymaster_blob[KEYMASTER_BLOB_SIZE];
+ __le32 keymaster_blob_size;
+
+ /* Store scrypt of salted intermediate key. When decryption fails, we can
+ check if this matches, and if it does, we know that the problem is with the
+ drive, and there is no point in asking the user for more passwords.
+
+ Note that if any part of this structure is corrupt, this will not match and
+ we will continue to believe the user entered the wrong password. In that
+ case the only solution is for the user to enter a password enough times to
+ force a wipe.
+
+ Note also that there is no need to worry about migration. If this data is
+ wrong, we simply won't recognise a right password, and will continue to
+ prompt. On the first password change, this value will be populated and
+ then we will be OK.
+ */
+ unsigned char scrypted_intermediate_key[SCRYPT_LEN];
+
+ /* sha of this structure with this element set to zero
+ Used when encrypting on reboot to validate structure before doing something
+ fatal
+ */
+ unsigned char sha256[SHA256_DIGEST_LENGTH];
+};
+
+/* Persistant data that should be available before decryption.
+ * Things like airplane mode, locale and timezone are kept
+ * here and can be retrieved by the CryptKeeper UI to properly
+ * configure the phone before asking for the password
+ * This is only valid if the major and minor version above
+ * is set to 1.1 or higher.
+ *
+ * This is a 4K structure. There are 2 copies, and the code alternates
+ * writing one and then clearing the previous one. The reading
+ * code reads the first valid copy it finds, based on the magic number.
+ * The absolute offset to the first of the two copies is kept in rev 1.1
+ * and higher crypt_mnt_ftr structures.
+ */
+struct crypt_persist_entry {
+ char key[PROPERTY_KEY_MAX];
+ char val[PROPERTY_VALUE_MAX];
+};
+
+/* Should be exactly 4K in size */
+struct crypt_persist_data {
+ __le32 persist_magic;
+ __le32 persist_valid_entries;
+ __le32 persist_spare[30];
+ struct crypt_persist_entry persist_entry[0];
+};
+
+#define DATA_MNT_POINT "/data"
+
+/* Return values for cryptfs_crypto_complete */
+#define CRYPTO_COMPLETE_NOT_ENCRYPTED 1
+#define CRYPTO_COMPLETE_ENCRYPTED 0
+#define CRYPTO_COMPLETE_BAD_METADATA (-1)
+#define CRYPTO_COMPLETE_PARTIAL (-2)
+#define CRYPTO_COMPLETE_INCONSISTENT (-3)
+#define CRYPTO_COMPLETE_CORRUPT (-4)
+
+/* Return values for cryptfs_enable_inplace*() */
+#define ENABLE_INPLACE_OK 0
+#define ENABLE_INPLACE_ERR_OTHER (-1)
+#define ENABLE_INPLACE_ERR_DEV (-2) /* crypto_blkdev issue */
+
+/* Return values for cryptfs_getfield */
+#define CRYPTO_GETFIELD_OK 0
+#define CRYPTO_GETFIELD_ERROR_NO_FIELD (-1)
+#define CRYPTO_GETFIELD_ERROR_OTHER (-2)
+#define CRYPTO_GETFIELD_ERROR_BUF_TOO_SMALL (-3)
+
+/* Return values for cryptfs_setfield */
+#define CRYPTO_SETFIELD_OK 0
+#define CRYPTO_SETFIELD_ERROR_OTHER (-1)
+#define CRYPTO_SETFIELD_ERROR_FIELD_TOO_LONG (-2)
+#define CRYPTO_SETFIELD_ERROR_VALUE_TOO_LONG (-3)
+
+/* Return values for persist_del_key */
+#define PERSIST_DEL_KEY_OK 0
+#define PERSIST_DEL_KEY_ERROR_OTHER (-1)
+#define PERSIST_DEL_KEY_ERROR_NO_FIELD (-2)
+
+int match_multi_entry(const char* key, const char* field, unsigned index);
+int wait_and_unmount(const char* mountpoint, bool kill);
+
+typedef int (*kdf_func)(const char* passwd, const unsigned char* salt, unsigned char* ikey,
+ void* params);
+
+int cryptfs_crypto_complete(void);
+int cryptfs_check_passwd(const char* pw);
+int cryptfs_verify_passwd(const char* pw);
+int cryptfs_restart(void);
+int cryptfs_enable(int type, const char* passwd, int no_ui);
+int cryptfs_changepw(int type, const char* newpw);
+int cryptfs_enable_default(int no_ui);
+int cryptfs_setup_ext_volume(const char* label, const char* real_blkdev, const unsigned char* key,
+ char* out_crypto_blkdev);
+int cryptfs_revert_ext_volume(const char* label);
+int cryptfs_getfield(const char* fieldname, char* value, int len);
+int cryptfs_setfield(const char* fieldname, const char* value);
+int cryptfs_mount_default_encrypted(void);
+int cryptfs_get_password_type(void);
+const char* cryptfs_get_password(void);
+void cryptfs_clear_password(void);
+int cryptfs_isConvertibleToFBE(void);
+
+uint32_t cryptfs_get_keysize();
+const char* cryptfs_get_crypto_name();
+
+#endif /* ANDROID_VOLD_CRYPTFS_H */
diff --git a/crypto/fscrypt/fscrypt_policy.cpp b/crypto/fscrypt/fscrypt_policy.cpp
new file mode 100755
index 0000000..43d9552
--- /dev/null
+++ b/crypto/fscrypt/fscrypt_policy.cpp
@@ -0,0 +1,389 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "fscrypt/fscrypt.h"
+
+#include <array>
+
+#include <asm/ioctl.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/fs.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <cutils/properties.h>
+#include <logwrap/logwrap.h>
+#include <utils/misc.h>
+
+#include "fscrypt_policy.h"
+
+static int encryption_mode = FS_ENCRYPTION_MODE_PRIVATE;
+
+bool fscrypt_is_native() {
+ char value[PROPERTY_VALUE_MAX];
+ property_get("ro.crypto.type", value, "none");
+ return !strcmp(value, "file");
+}
+
+static void log_ls(const char* dirname) {
+ std::array<const char*, 3> argv = {"ls", "-laZ", dirname};
+ int status = 0;
+ auto res =
+ android_fork_execvp(argv.size(), const_cast<char**>(argv.data()), &status, false, true);
+ if (res != 0) {
+ PLOG(ERROR) << argv[0] << " " << argv[1] << " " << argv[2] << "failed";
+ return;
+ }
+ if (!WIFEXITED(status)) {
+ LOG(ERROR) << argv[0] << " " << argv[1] << " " << argv[2]
+ << " did not exit normally, status: " << status;
+ return;
+ }
+ if (WEXITSTATUS(status) != 0) {
+ LOG(ERROR) << argv[0] << " " << argv[1] << " " << argv[2]
+ << " returned failure: " << WEXITSTATUS(status);
+ return;
+ }
+}
+
+extern "C" void policy_to_hex(const uint8_t* policy, char* hex) {
+ for (size_t i = 0, j = 0; i < FS_KEY_DESCRIPTOR_SIZE; i++) {
+ hex[j++] = HEX_LOOKUP[(policy[i] & 0xF0) >> 4];
+ hex[j++] = HEX_LOOKUP[policy[i] & 0x0F];
+ }
+ hex[FS_KEY_DESCRIPTOR_SIZE_HEX - 1] = '\0';
+}
+
+static bool is_dir_empty(const char *dirname, bool *is_empty)
+{
+ int n = 0;
+ auto dirp = std::unique_ptr<DIR, int (*)(DIR*)>(opendir(dirname), closedir);
+ if (!dirp) {
+ PLOG(ERROR) << "Unable to read directory: " << dirname;
+ return false;
+ }
+ for (;;) {
+ errno = 0;
+ auto entry = readdir(dirp.get());
+ if (!entry) {
+ if (errno) {
+ PLOG(ERROR) << "Unable to read directory: " << dirname;
+ return false;
+ }
+ break;
+ }
+ if (strcmp(entry->d_name, "lost+found") != 0) { // Skip lost+found
+ ++n;
+ if (n > 2) {
+ *is_empty = false;
+ return true;
+ }
+ }
+ }
+ *is_empty = true;
+ return true;
+}
+
+static uint8_t fscrypt_get_policy_flags(int filenames_encryption_mode) {
+ if (filenames_encryption_mode == FS_ENCRYPTION_MODE_AES_256_CTS) {
+ // Use legacy padding with our original filenames encryption mode.
+ return FS_POLICY_FLAGS_PAD_4;
+ } else if (filenames_encryption_mode == FS_ENCRYPTION_MODE_ADIANTUM) {
+ // Use DIRECT_KEY for Adiantum, since it's much more efficient but just
+ // as secure since Android doesn't reuse the same master key for
+ // multiple encryption modes
+ return (FS_POLICY_FLAGS_PAD_16 | FS_POLICY_FLAG_DIRECT_KEY);
+ }
+ // With a new mode we can use the better padding flag without breaking existing devices: pad
+ // filenames with zeroes to the next 16-byte boundary. This is more secure (helps hide the
+ // length of filenames) and makes the inputs evenly divisible into blocks which is more
+ // efficient for encryption and decryption.
+ return FS_POLICY_FLAGS_PAD_16;
+}
+
+static bool fscrypt_policy_set(const char *directory, uint8_t *policy,
+ size_t policy_length,
+ int contents_encryption_mode,
+ int filenames_encryption_mode) {
+ if (policy_length != FS_KEY_DESCRIPTOR_SIZE) {
+ LOG(ERROR) << "Policy wrong length: " << policy_length;
+ return false;
+ }
+ char policy_hex[FS_KEY_DESCRIPTOR_SIZE_HEX];
+ policy_to_hex(policy, policy_hex);
+
+ int fd = open(directory, O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC);
+ if (fd == -1) {
+ PLOG(ERROR) << "Failed to open directory " << directory;
+ return false;
+ }
+
+ fscrypt_policy fp;
+ fp.version = 0;
+ fp.contents_encryption_mode = contents_encryption_mode;
+ fp.filenames_encryption_mode = filenames_encryption_mode;
+ fp.flags = fscrypt_get_policy_flags(filenames_encryption_mode);
+ memcpy(fp.master_key_descriptor, policy, FS_KEY_DESCRIPTOR_SIZE);
+ if (ioctl(fd, FS_IOC_SET_ENCRYPTION_POLICY, &fp)) {
+ PLOG(ERROR) << "Failed to set encryption policy for " << directory << " to " << policy_hex
+ << " modes " << contents_encryption_mode << "/" << filenames_encryption_mode;
+ close(fd);
+ return false;
+ }
+ close(fd);
+
+ LOG(INFO) << "Policy for " << directory << " set to " << policy_hex
+ << " modes " << contents_encryption_mode << "/" << filenames_encryption_mode;
+ return true;
+}
+
+static bool fscrypt_policy_get(const char *directory, uint8_t *policy,
+ size_t policy_length,
+ int contents_encryption_mode,
+ int filenames_encryption_mode) {
+ if (policy_length != FS_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;
+ }
+
+ fscrypt_policy fp;
+ memset(&fp, 0, sizeof(fscrypt_policy));
+ if (ioctl(fd, FS_IOC_GET_ENCRYPTION_POLICY, &fp) != 0) {
+ PLOG(ERROR) << "Failed to get encryption policy for " << directory;
+ close(fd);
+ log_ls(directory);
+ return false;
+ }
+ close(fd);
+
+ if ((fp.version != 0)
+ || (fp.contents_encryption_mode != contents_encryption_mode)
+ || (fp.filenames_encryption_mode != filenames_encryption_mode)
+ || (fp.flags !=
+ fscrypt_get_policy_flags(filenames_encryption_mode))) {
+ LOG(ERROR) << "Failed to find matching encryption policy for " << directory;
+ return false;
+ }
+ memcpy(policy, fp.master_key_descriptor, FS_KEY_DESCRIPTOR_SIZE);
+
+ return true;
+}
+
+static bool fscrypt_policy_check(const char *directory, uint8_t *policy,
+ size_t policy_length,
+ int contents_encryption_mode,
+ int filenames_encryption_mode) {
+ if (policy_length != FS_KEY_DESCRIPTOR_SIZE) {
+ LOG(ERROR) << "Policy wrong length: " << policy_length;
+ return false;
+ }
+ uint8_t existing_policy[FS_KEY_DESCRIPTOR_SIZE];
+ if (!fscrypt_policy_get(directory, existing_policy, FS_KEY_DESCRIPTOR_SIZE,
+ contents_encryption_mode,
+ filenames_encryption_mode)) return false;
+ char existing_policy_hex[FS_KEY_DESCRIPTOR_SIZE_HEX];
+
+ policy_to_hex(existing_policy, existing_policy_hex);
+
+ if (memcmp(policy, existing_policy, FS_KEY_DESCRIPTOR_SIZE) != 0) {
+ char policy_hex[FS_KEY_DESCRIPTOR_SIZE_HEX];
+ policy_to_hex(policy, policy_hex);
+ LOG(ERROR) << "Found policy " << existing_policy_hex << " at " << directory
+ << " which doesn't match expected value " << policy_hex;
+ log_ls(directory);
+ return false;
+ }
+ LOG(INFO) << "Found policy " << existing_policy_hex << " at " << directory
+ << " which matches expected value";
+ return true;
+}
+
+int fscrypt_policy_ensure(const char *directory, uint8_t *policy,
+ size_t policy_length,
+ const char *contents_encryption_mode,
+ const char *filenames_encryption_mode) {
+ int contents_mode = 0;
+ int filenames_mode = 0;
+
+ if (!strcmp(contents_encryption_mode, "software") ||
+ !strcmp(contents_encryption_mode, "aes-256-xts")) {
+ contents_mode = FS_ENCRYPTION_MODE_AES_256_XTS;
+ } else if (!strcmp(contents_encryption_mode, "adiantum")) {
+ contents_mode = FS_ENCRYPTION_MODE_ADIANTUM;
+ } else if (!strcmp(contents_encryption_mode, "ice")) {
+ contents_mode = FS_ENCRYPTION_MODE_PRIVATE;
+ } else {
+ LOG(ERROR) << "Invalid file contents encryption mode: "
+ << contents_encryption_mode;
+ return -1;
+ }
+
+ if (!strcmp(filenames_encryption_mode, "aes-256-cts")) {
+ filenames_mode = FS_ENCRYPTION_MODE_AES_256_CTS;
+ } else if (!strcmp(filenames_encryption_mode, "aes-256-heh")) {
+ filenames_mode = FS_ENCRYPTION_MODE_AES_256_HEH;
+ } else if (!strcmp(filenames_encryption_mode, "adiantum")) {
+ filenames_mode = FS_ENCRYPTION_MODE_ADIANTUM;
+ } else {
+ LOG(ERROR) << "Invalid file names encryption mode: "
+ << filenames_encryption_mode;
+ return -1;
+ }
+
+ bool is_empty;
+ if (!is_dir_empty(directory, &is_empty)) return -1;
+ if (is_empty) {
+ if (!fscrypt_policy_set(directory, policy, policy_length,
+ contents_mode, filenames_mode)) return -1;
+ } else {
+ if (!fscrypt_policy_check(directory, policy, policy_length,
+ contents_mode, filenames_mode)) return -1;
+ }
+ return 0;
+}
+
+extern "C" bool fscrypt_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);
+
+ std::string contents_encryption_mode_string = std::string(contents_encryption_mode);
+ int pos = contents_encryption_mode_string.find(":");
+ PLOG(ERROR) << "contents_encryption_mode_string: " << contents_encryption_mode_string.substr(0, pos);
+
+ // if (!strcmp(contents_encryption_mode, "software")) {
+ if (contents_encryption_mode_string.substr(0, pos) == "software") {
+ encryption_mode = FS_ENCRYPTION_MODE_AES_256_XTS;
+ // } else if (!strcmp(contents_encryption_mode, "ice")) {
+ } else if (contents_encryption_mode_string.substr(0, pos) == "ice") {
+ encryption_mode = FS_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;
+}
+
+extern "C" void fscrypt_policy_fill_default_struct(fscrypt_encryption_policy *fep) {
+ fep->version = 0;
+ fep->contents_encryption_mode = encryption_mode;
+ fep->filenames_encryption_mode = FS_ENCRYPTION_MODE_AES_256_CTS;
+ fep->flags = 0;
+ memset((void*)&fep->master_key_descriptor[0], 0, FS_KEY_DESCRIPTOR_SIZE);
+}
+
+extern "C" bool fscrypt_policy_set_struct(const char *directory, const fscrypt_encryption_policy *fep) {
+ 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, FS_IOC_SET_ENCRYPTION_POLICY, fep)) {
+ 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 fscrypt_policy_get_struct(const char *directory, fscrypt_encryption_policy *fep) {
+ 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(fep, 0, sizeof(fscrypt_encryption_policy));
+ if (ioctl(fd, FS_IOC_GET_ENCRYPTION_POLICY, fep) != 0) {
+ PLOG(ERROR) << "Failed to get encryption policy for " << directory;
+ close(fd);
+ return false;
+ }
+ printf("fscrypt_policy_get_struct::fep->version::%d\n", fep->version);
+ close(fd);
+ return true;
+}
+
+extern "C" bool fscrypt_policy_set(const char *directory, uint8_t *policy,
+ size_t policy_length, int contents_encryption_mode) {
+ if (contents_encryption_mode == 0)
+ contents_encryption_mode = encryption_mode;
+ if (policy_length != FS_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;
+ }
+
+ fscrypt_encryption_policy fep;
+ fep.version = 0;
+ fep.contents_encryption_mode = contents_encryption_mode;
+ fep.filenames_encryption_mode = FS_ENCRYPTION_MODE_AES_256_CTS;
+ fep.flags = 0;
+ memcpy(fep.master_key_descriptor, policy, FS_KEY_DESCRIPTOR_SIZE);
+ if (ioctl(fd, FS_IOC_SET_ENCRYPTION_POLICY, &fep)) {
+ 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[FS_KEY_DESCRIPTOR_SIZE_HEX];
+ policy_to_hex(policy, policy_hex);
+ LOG(INFO) << "Policy for " << directory << " set to " << policy_hex;
+ return true;
+}
diff --git a/crypto/fscrypt/fscrypt_policy.h b/crypto/fscrypt/fscrypt_policy.h
new file mode 100755
index 0000000..01fc419
--- /dev/null
+++ b/crypto/fscrypt/fscrypt_policy.h
@@ -0,0 +1,63 @@
+/*
+ * 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 _FS_CRYPT_H_
+#define _FS_CRYPT_H_
+
+#include <sys/cdefs.h>
+#include <stdbool.h>
+#include <cutils/multiuser.h>
+#include <linux/fs.h>
+
+__BEGIN_DECLS
+
+#define FS_KEY_DESCRIPTOR_SIZE_HEX (2 * FS_KEY_DESCRIPTOR_SIZE + 1)
+
+/* modes not supported by upstream kernel, so not in <linux/fs.h> */
+#define FS_ENCRYPTION_MODE_AES_256_HEH 126
+#define FS_ENCRYPTION_MODE_PRIVATE 127
+
+/* new definition, not yet in Bionic's <linux/fs.h> */
+#ifndef FS_ENCRYPTION_MODE_ADIANTUM
+#define FS_ENCRYPTION_MODE_ADIANTUM 9
+#endif
+
+/* new definition, not yet in Bionic's <linux/fs.h> */
+#ifndef FS_POLICY_FLAG_DIRECT_KEY
+#define FS_POLICY_FLAG_DIRECT_KEY 0x4
+#endif
+
+#define HEX_LOOKUP "0123456789abcdef"
+
+struct fscrypt_encryption_policy {
+ uint8_t version;
+ uint8_t contents_encryption_mode;
+ uint8_t filenames_encryption_mode;
+ uint8_t flags;
+ uint8_t master_key_descriptor[FS_KEY_DESCRIPTOR_SIZE];
+} __attribute__((packed));
+
+
+bool fscrypt_set_mode();
+bool lookup_ref_key(const uint8_t *policy, uint8_t* policy_type);
+bool lookup_ref_tar(const uint8_t *policy_type, uint8_t *policy);
+void policy_to_hex(const uint8_t* policy, char* hex);
+bool fscrypt_policy_get_struct(const char *directory, struct fscrypt_encryption_policy *fep);
+bool fscrypt_policy_set_struct(const char *directory, const struct fscrypt_encryption_policy *fep);
+void fscrypt_policy_fill_default_struct(struct fscrypt_encryption_policy *fep);
+__END_DECLS
+
+#endif // _FS_CRYPT_H_
diff --git a/crypto/fscrypt/fscryptpolicyget.cpp b/crypto/fscrypt/fscryptpolicyget.cpp
new file mode 100755
index 0000000..5add616
--- /dev/null
+++ b/crypto/fscrypt/fscryptpolicyget.cpp
@@ -0,0 +1,39 @@
+/*
+ * 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 "fscrypt_policy.h"
+
+#define FS_KEY_DESCRIPTOR_SIZE 8
+
+int main(int argc, char *argv[]) {
+ if (argc != 2) {
+ printf("Must specify a path\n");
+ return -1;
+ } else {
+ fscrypt_encryption_policy fep;
+ if (fscrypt_policy_get_struct(argv[1], &fep)) {
+ char policy_hex[FS_KEY_DESCRIPTOR_SIZE_HEX];
+ policy_to_hex(fep.master_key_descriptor, policy_hex);
+ printf("%s\n", policy_hex);
+ } else {
+ printf("No policy set\n");
+ }
+ }
+ return 0;
+}
diff --git a/crypto/fscrypt/keystore_auth.cpp b/crypto/fscrypt/keystore_auth.cpp
new file mode 100755
index 0000000..a65fe21
--- /dev/null
+++ b/crypto/fscrypt/keystore_auth.cpp
@@ -0,0 +1,108 @@
+/*
+ Copyright 2020 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/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>
+
+#ifndef LOG_TAG
+#define LOG_TAG "keystore_auth"
+#endif
+
+using namespace android;
+using android::security::keystore::IKeystoreService;
+
+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<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");
+ 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/fscrypt/main.cpp b/crypto/fscrypt/main.cpp
new file mode 100644
index 0000000..f0266ae
--- /dev/null
+++ b/crypto/fscrypt/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/fscrypt/sehandle.h b/crypto/fscrypt/sehandle.h
new file mode 100644
index 0000000..8921db5
--- /dev/null
+++ b/crypto/fscrypt/sehandle.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2014 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 _SEHANDLE_H
+#define _SEHANDLE_H
+
+#include <selinux/android.h>
+
+extern struct selabel_handle* sehandle;
+
+#endif