From e9afc3de0fe1df795cc2f8cfab489177252812be Mon Sep 17 00:00:00 2001 From: Ethan Yonker Date: Thu, 30 Aug 2018 15:16:27 -0500 Subject: Decrypt FBE on 9.0 (backwards compatible) Building in 9.0 may require you to add a flag to your twrp fstab with the fileencryption details like: fileencryption=ice:aes-256-heh Verify this against your device's stock fstab of course. Change-Id: If9286f5d5787280814daca9fbc8f5191ff26a839 --- crypto/ext4crypt/Android.mk | 41 +- crypto/ext4crypt/Decrypt.cpp | 101 ++- crypto/ext4crypt/Decrypt.h | 4 + crypto/ext4crypt/Ext4Crypt.cpp | 4 + crypto/ext4crypt/Ext4CryptPie.cpp | 861 ++++++++++++++++++++++++ crypto/ext4crypt/Ext4CryptPie.h | 42 ++ crypto/ext4crypt/KeyBuffer.cpp | 37 + crypto/ext4crypt/KeyBuffer.h | 63 ++ crypto/ext4crypt/KeyStorage4.cpp | 598 ++++++++++++++++ crypto/ext4crypt/KeyStorage4.h | 73 ++ crypto/ext4crypt/KeyUtil.cpp | 215 ++++++ crypto/ext4crypt/KeyUtil.h | 41 ++ crypto/ext4crypt/Keymaster4.cpp | 352 ++++++++++ crypto/ext4crypt/Keymaster4.h | 159 +++++ crypto/ext4crypt/Utils.cpp | 34 +- crypto/ext4crypt/Utils.h | 3 + crypto/ext4crypt/Weaver1.cpp | 6 +- crypto/ext4crypt/keystore_auth.cpp | 21 +- mtp/Android.mk | 2 +- partition.cpp | 20 + prebuilt/Android.mk | 13 +- res-hdpi/images/erasing_text.png | Bin 51214 -> 0 bytes res-hdpi/images/error_text.png | Bin 36610 -> 0 bytes res-hdpi/images/installing_security_text.png | Bin 115058 -> 0 bytes res-hdpi/images/installing_text.png | Bin 105531 -> 0 bytes res-hdpi/images/no_command_text.png | Bin 62331 -> 0 bytes res-mdpi/images/erasing_text.png | Bin 30027 -> 0 bytes res-mdpi/images/error_text.png | Bin 21546 -> 0 bytes res-mdpi/images/installing_security_text.png | Bin 70491 -> 0 bytes res-mdpi/images/installing_text.png | Bin 62100 -> 0 bytes res-mdpi/images/no_command_text.png | Bin 34772 -> 0 bytes res-xhdpi/images/erasing_text.png | Bin 74450 -> 0 bytes res-xhdpi/images/error_text.png | Bin 53331 -> 0 bytes res-xhdpi/images/installing_security_text.png | Bin 201207 -> 0 bytes res-xhdpi/images/installing_text.png | Bin 178087 -> 0 bytes res-xhdpi/images/no_command_text.png | Bin 88037 -> 0 bytes res-xxhdpi/images/erasing_text.png | Bin 123362 -> 0 bytes res-xxhdpi/images/error_text.png | Bin 86841 -> 0 bytes res-xxhdpi/images/installing_security_text.png | Bin 453013 -> 0 bytes res-xxhdpi/images/installing_text.png | Bin 420313 -> 0 bytes res-xxhdpi/images/no_command_text.png | Bin 247462 -> 0 bytes res-xxxhdpi/images/erasing_text.png | Bin 267640 -> 0 bytes res-xxxhdpi/images/error_text.png | Bin 181450 -> 0 bytes res-xxxhdpi/images/installing_security_text.png | Bin 618212 -> 0 bytes res-xxxhdpi/images/installing_text.png | Bin 574440 -> 0 bytes res-xxxhdpi/images/no_command_text.png | Bin 336495 -> 0 bytes 46 files changed, 2643 insertions(+), 47 deletions(-) create mode 100644 crypto/ext4crypt/Ext4CryptPie.cpp create mode 100644 crypto/ext4crypt/Ext4CryptPie.h create mode 100644 crypto/ext4crypt/KeyBuffer.cpp create mode 100644 crypto/ext4crypt/KeyBuffer.h create mode 100644 crypto/ext4crypt/KeyStorage4.cpp create mode 100644 crypto/ext4crypt/KeyStorage4.h create mode 100644 crypto/ext4crypt/KeyUtil.cpp create mode 100644 crypto/ext4crypt/KeyUtil.h create mode 100644 crypto/ext4crypt/Keymaster4.cpp create mode 100644 crypto/ext4crypt/Keymaster4.h delete mode 100644 res-hdpi/images/erasing_text.png delete mode 100644 res-hdpi/images/error_text.png delete mode 100644 res-hdpi/images/installing_security_text.png delete mode 100644 res-hdpi/images/installing_text.png delete mode 100644 res-hdpi/images/no_command_text.png delete mode 100644 res-mdpi/images/erasing_text.png delete mode 100644 res-mdpi/images/error_text.png delete mode 100644 res-mdpi/images/installing_security_text.png delete mode 100644 res-mdpi/images/installing_text.png delete mode 100644 res-mdpi/images/no_command_text.png delete mode 100644 res-xhdpi/images/erasing_text.png delete mode 100644 res-xhdpi/images/error_text.png delete mode 100644 res-xhdpi/images/installing_security_text.png delete mode 100644 res-xhdpi/images/installing_text.png delete mode 100644 res-xhdpi/images/no_command_text.png delete mode 100644 res-xxhdpi/images/erasing_text.png delete mode 100644 res-xxhdpi/images/error_text.png delete mode 100644 res-xxhdpi/images/installing_security_text.png delete mode 100644 res-xxhdpi/images/installing_text.png delete mode 100644 res-xxhdpi/images/no_command_text.png delete mode 100644 res-xxxhdpi/images/erasing_text.png delete mode 100644 res-xxxhdpi/images/error_text.png delete mode 100644 res-xxxhdpi/images/installing_security_text.png delete mode 100644 res-xxxhdpi/images/installing_text.png delete mode 100644 res-xxxhdpi/images/no_command_text.png diff --git a/crypto/ext4crypt/Android.mk b/crypto/ext4crypt/Android.mk index 4aba9ef45..e589903d9 100644 --- a/crypto/ext4crypt/Android.mk +++ b/crypto/ext4crypt/Android.mk @@ -5,7 +5,7 @@ include $(CLEAR_VARS) LOCAL_MODULE := libe4crypt LOCAL_MODULE_TAGS := eng optional LOCAL_CFLAGS := -LOCAL_SRC_FILES := Decrypt.cpp Ext4Crypt.cpp ScryptParameters.cpp Utils.cpp HashPassword.cpp ext4_crypt.cpp +LOCAL_SRC_FILES := Decrypt.cpp ScryptParameters.cpp Utils.cpp HashPassword.cpp ext4_crypt.cpp LOCAL_SHARED_LIBRARIES := libselinux libc libc++ libext4_utils libbase libcrypto libcutils libkeymaster_messages libhardware libprotobuf-cpp-lite LOCAL_STATIC_LIBRARIES := libscrypt_static LOCAL_C_INCLUDES := system/extras/ext4_utils system/extras/ext4_utils/include/ext4_utils external/scrypt/lib/crypto system/security/keystore hardware/libhardware/include/hardware system/security/softkeymaster/include/keymaster system/keymaster/include @@ -15,22 +15,40 @@ ifneq ($(wildcard hardware/libhardware/include/hardware/keymaster0.h),) LOCAL_C_INCLUDES += external/boringssl/src/include endif ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 26; echo $$?),0) - LOCAL_CFLAGS += -DUSE_KEYSTORAGE_3 -DHAVE_GATEKEEPER1 - LOCAL_SRC_FILES += Keymaster3.cpp KeyStorage3.cpp - LOCAL_SHARED_LIBRARIES += android.hardware.keymaster@3.0 libkeystore_binder libhidlbase libutils libbinder - LOCAL_SHARED_LIBRARIES += android.hardware.gatekeeper@1.0 - ifneq ($(wildcard hardware/interfaces/weaver/Android.bp),) + #8.0 or higher + LOCAL_CFLAGS += -DHAVE_GATEKEEPER1 + LOCAL_SHARED_LIBRARIES += android.hardware.keymaster@3.0 libkeystore_binder libhidlbase libutils libbinder android.hardware.gatekeeper@1.0 + ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 28; echo $$?),0) + #9.0 rules + LOCAL_CFLAGS += -DUSE_KEYSTORAGE_4 -Wno-unused-variable -Wno-sign-compare -Wno-unused-parameter -Wno-comment + LOCAL_SRC_FILES += Ext4CryptPie.cpp Keymaster4.cpp KeyStorage4.cpp KeyUtil.cpp + LOCAL_SHARED_LIBRARIES += android.hardware.keymaster@4.0 libkeymaster4support + LOCAL_SHARED_LIBRARIES += android.hardware.gatekeeper@1.0 libkeystore_parcelables libkeystore_aidl LOCAL_CFLAGS += -DHAVE_SYNTH_PWD_SUPPORT LOCAL_SRC_FILES += Weaver1.cpp LOCAL_SHARED_LIBRARIES += android.hardware.weaver@1.0 - endif - ifneq ($(wildcard system/core/libkeyutils/Android.bp),) LOCAL_CFLAGS += -DHAVE_LIBKEYUTILS LOCAL_SHARED_LIBRARIES += libkeyutils + else + #8.0 rules + LOCAL_CFLAGS += -DUSE_KEYSTORAGE_3 + LOCAL_SRC_FILES += Ext4Crypt.cpp Keymaster3.cpp KeyStorage3.cpp + ifneq ($(wildcard hardware/interfaces/weaver/Android.bp),) + #only present in some 8.0 trees and should be in all 8.1 trees + LOCAL_CFLAGS += -DHAVE_SYNTH_PWD_SUPPORT + LOCAL_SRC_FILES += Weaver1.cpp + LOCAL_SHARED_LIBRARIES += android.hardware.weaver@1.0 + endif + ifneq ($(wildcard system/core/libkeyutils/Android.bp),) + #only present in some 8.0 trees and should be in all 8.1 trees + LOCAL_CFLAGS += -DHAVE_LIBKEYUTILS + LOCAL_SHARED_LIBRARIES += libkeyutils + endif endif LOCAL_REQUIRED_MODULES := keystore_auth else - LOCAL_SRC_FILES += Keymaster.cpp KeyStorage.cpp + #7.x rules + LOCAL_SRC_FILES += Ext4Crypt.cpp Keymaster.cpp KeyStorage.cpp endif ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 28; echo $$?),0) LOCAL_SHARED_LIBRARIES += libsoftkeymaster @@ -69,6 +87,11 @@ 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 +ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 28; echo $$?),0) + #9.0 + LOCAL_CFLAGS += -DUSE_SECURITY_NAMESPACE + LOCAL_SHARED_LIBRARIES += libkeystore_aidl +endif LOCAL_LDFLAGS += -Wl,-dynamic-linker,/sbin/linker64 include $(BUILD_EXECUTABLE) diff --git a/crypto/ext4crypt/Decrypt.cpp b/crypto/ext4crypt/Decrypt.cpp index c062f8ae4..3eeaaf877 100644 --- a/crypto/ext4crypt/Decrypt.cpp +++ b/crypto/ext4crypt/Decrypt.cpp @@ -15,7 +15,11 @@ */ #include "Decrypt.h" +#ifdef USE_KEYSTORAGE_4 +#include "Ext4CryptPie.h" +#else #include "Ext4Crypt.h" +#endif #include #include @@ -50,12 +54,16 @@ #include +#ifdef USE_KEYSTORAGE_4 +#include +#else #include +#include +#endif #include #include #include -#include #include extern "C" { @@ -437,6 +445,9 @@ namespace keystore { #define SYNTHETIC_PASSWORD_VERSION 2 #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 @@ -447,13 +458,19 @@ namespace keystore { * 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) { +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); @@ -475,6 +492,7 @@ bool Find_Keystore_Alias_SubID_And_Prep_Files(const userid_t user_id, std::strin 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) @@ -483,14 +501,25 @@ bool Find_Keystore_Alias_SubID_And_Prep_Files(const userid_t user_id, std::strin size_t len = strlen(de->d_name); if (len <= prefix_len) continue; - if (!strstr(de->d_name, SYNTHETIC_PASSWORD_KEY_PREFIX)) + 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; - 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("keystoreid: '%s'\n", keystoreid.c_str()); + 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; @@ -508,6 +537,8 @@ bool Find_Keystore_Alias_SubID_And_Prep_Files(const userid_t user_id, std::strin dstof.close(); } closedir(dir); + if (!found_subid && !mKey_Prefix.empty() && !keystoreid.empty()) + found_subid = true; return found_subid; } @@ -518,14 +549,18 @@ std::string unwrapSyntheticPasswordBlob(const std::string& spblob_path, const st std::string disk_decryption_secret_key = ""; std::string keystore_alias_subid; - if (!Find_Keystore_Alias_SubID_And_Prep_Files(user_id, 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 sp binder = getKeystoreBinderRetry(); +#ifdef USE_KEYSTORAGE_4 + sp service = interface_cast(binder); +#else sp service = interface_cast(binder); +#endif if (service == NULL) { printf("error: could not connect to keystore service\n"); return disk_decryption_secret_key; @@ -682,13 +717,27 @@ std::string unwrapSyntheticPasswordBlob(const std::string& spblob_path, const st //keymasterArgs.addUnsignedInt(KeymasterDefs.KM_TAG_MAC_LENGTH, mTagLengthBits); ::keystore::hidl_vec entropy; // No entropy is needed for decrypt entropy.resize(0); - std::string keystore_alias = SYNTHETIC_PASSWORD_KEY_PREFIX; + std::string keystore_alias = mKey_Prefix; keystore_alias += keystore_alias_subid; String16 keystore_alias16(keystore_alias.c_str()); +#ifdef USE_KEYSTORAGE_4 + android::hardware::keymaster::V4_0::KeyPurpose purpose = android::hardware::keymaster::V4_0::KeyPurpose::DECRYPT; + security::keymaster::OperationResult begin_result; + security::keymaster::OperationResult update_result; + security::keymaster::OperationResult finish_result; + ::android::security::keymaster::KeymasterArguments empty_params; + // These parameters are mostly driven by the cipher.init call https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java#63 + service->begin(binder, keystore_alias16, (int32_t)purpose, true, android::security::keymaster::KeymasterArguments(begin_params.hidl_data()), entropy, -1, &begin_result); +#else ::keystore::KeyPurpose purpose = ::keystore::KeyPurpose::DECRYPT; OperationResult begin_result; + OperationResult update_result; + OperationResult finish_result; + ::keystore::hidl_vec<::keystore::KeyParameter> empty_params; + empty_params.resize(0); // These parameters are mostly driven by the cipher.init call https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java#63 service->begin(binder, keystore_alias16, purpose, true, begin_params.hidl_data(), entropy, -1, &begin_result); +#endif ret = begin_result.resultCode; if (ret != 1 /*android::keystore::ResponseCode::NO_ERROR*/) { printf("keystore begin error: (%d)\n", /*responses[ret],*/ ret); @@ -696,9 +745,6 @@ std::string unwrapSyntheticPasswordBlob(const std::string& spblob_path, const st } else { //printf("keystore begin operation successful\n"); } - ::keystore::hidl_vec<::keystore::KeyParameter> empty_params; - empty_params.resize(0); - OperationResult update_result; // The cipher.doFinal call triggers an update to the keystore followed by a finish https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java#64 // See also https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java#208 service->update(begin_result.token, empty_params, intermediate_key, &update_result); @@ -716,7 +762,6 @@ std::string unwrapSyntheticPasswordBlob(const std::string& spblob_path, const st disk_decryption_secret_key = PersonalizedHash(PERSONALIZATION_FBE_KEY, (const char*)&update_result.data[0], update_result.data.size()); //printf("disk_decryption_secret_key: '%s'\n", disk_decryption_secret_key.c_str()); ::keystore::hidl_vec signature; - OperationResult finish_result; service->finish(begin_result.token, empty_params, signature, entropy, &finish_result); ret = finish_result.resultCode; if (ret != 1 /*android::keystore::ResponseCode::NO_ERROR*/) { @@ -785,13 +830,27 @@ std::string unwrapSyntheticPasswordBlob(const std::string& spblob_path, const st begin_params.Authorization(::keystore::TAG_MAC_LENGTH, maclen); ::keystore::hidl_vec entropy; // No entropy is needed for decrypt entropy.resize(0); - std::string keystore_alias = SYNTHETIC_PASSWORD_KEY_PREFIX; + std::string keystore_alias = mKey_Prefix; keystore_alias += keystore_alias_subid; String16 keystore_alias16(keystore_alias.c_str()); +#ifdef USE_KEYSTORAGE_4 + android::hardware::keymaster::V4_0::KeyPurpose purpose = android::hardware::keymaster::V4_0::KeyPurpose::DECRYPT; + security::keymaster::OperationResult begin_result; + security::keymaster::OperationResult update_result; + security::keymaster::OperationResult finish_result; + ::android::security::keymaster::KeymasterArguments empty_params; + // These parameters are mostly driven by the cipher.init call https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java#63 + service->begin(binder, keystore_alias16, (int32_t)purpose, true, android::security::keymaster::KeymasterArguments(begin_params.hidl_data()), entropy, -1, &begin_result); +#else ::keystore::KeyPurpose purpose = ::keystore::KeyPurpose::DECRYPT; OperationResult begin_result; + OperationResult update_result; + OperationResult finish_result; + ::keystore::hidl_vec<::keystore::KeyParameter> empty_params; + empty_params.resize(0); // These parameters are mostly driven by the cipher.init call https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java#63 service->begin(binder, keystore_alias16, purpose, true, begin_params.hidl_data(), entropy, -1, &begin_result); +#endif ret = begin_result.resultCode; if (ret != 1 /*android::keystore::ResponseCode::NO_ERROR*/) { printf("keystore begin error: (%d)\n", /*responses[ret],*/ ret); @@ -799,9 +858,6 @@ std::string unwrapSyntheticPasswordBlob(const std::string& spblob_path, const st } /*else { printf("keystore begin operation successful\n"); }*/ - ::keystore::hidl_vec<::keystore::KeyParameter> empty_params; - empty_params.resize(0); - OperationResult update_result; // The cipher.doFinal call triggers an update to the keystore followed by a finish https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java#64 // See also https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java#208 service->update(begin_result.token, empty_params, cipher_text_hidlvec, &update_result); @@ -824,7 +880,6 @@ std::string unwrapSyntheticPasswordBlob(const std::string& spblob_path, const st memcpy(keystore_result, &update_result.data[0], update_result.data.size()); //printf("keystore_result data: "); output_hex(keystore_result, keystore_result_size); printf("\n"); ::keystore::hidl_vec signature; - OperationResult finish_result; service->finish(begin_result.token, empty_params, signature, entropy, &finish_result); ret = finish_result.resultCode; if (ret != 1 /*android::keystore::ResponseCode::NO_ERROR*/) { @@ -1103,7 +1158,11 @@ bool Decrypt_User_Synth_Pass(const userid_t user_id, const std::string& Password printf("e4crypt_unlock_user_key returned fail\n"); return Free_Return(retval, weaver_key, &pwd); } +#ifdef USE_KEYSTORAGE_4 + if (!e4crypt_prepare_user_storage("", user_id, 0, flags)) { +#else if (!e4crypt_prepare_user_storage(nullptr, user_id, 0, flags)) { +#endif printf("failed to e4crypt_prepare_user_storage\n"); return Free_Return(retval, weaver_key, &pwd); } @@ -1189,7 +1248,11 @@ bool Decrypt_User(const userid_t user_id, const std::string& Password) { printf("e4crypt_unlock_user_key returned fail\n"); return false; } +#ifdef USE_KEYSTORAGE_4 + if (!e4crypt_prepare_user_storage("", user_id, 0, flags)) { +#else if (!e4crypt_prepare_user_storage(nullptr, user_id, 0, flags)) { +#endif printf("failed to e4crypt_prepare_user_storage\n"); return false; } diff --git a/crypto/ext4crypt/Decrypt.h b/crypto/ext4crypt/Decrypt.h index c05ac69b9..56318e278 100644 --- a/crypto/ext4crypt/Decrypt.h +++ b/crypto/ext4crypt/Decrypt.h @@ -26,6 +26,10 @@ __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(); diff --git a/crypto/ext4crypt/Ext4Crypt.cpp b/crypto/ext4crypt/Ext4Crypt.cpp index 048a0bd9f..15d7d8f32 100644 --- a/crypto/ext4crypt/Ext4Crypt.cpp +++ b/crypto/ext4crypt/Ext4Crypt.cpp @@ -17,7 +17,11 @@ #include "Ext4Crypt.h" #include "Decrypt.h" +#ifdef USE_KEYSTORAGE_3 +#include "KeyStorage3.h" +#else #include "KeyStorage.h" +#endif #include "Utils.h" #include diff --git a/crypto/ext4crypt/Ext4CryptPie.cpp b/crypto/ext4crypt/Ext4CryptPie.cpp new file mode 100644 index 000000000..615229ea9 --- /dev/null +++ b/crypto/ext4crypt/Ext4CryptPie.cpp @@ -0,0 +1,861 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Ext4CryptPie.h" + +#include "KeyStorage4.h" +#include "KeyUtil.h" +#include "Utils.h" +#include "Decrypt.h" +//#include "VoldUtil.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +//#include "android/os/IVold.h" + +//#include "cryptfs.h" + +#define EMULATED_USES_SELINUX 0 +#define MANAGE_MISC_DIRS 0 + +#include +#include + +#include +#include + +#include +//#include +#include +#include + +#include +#define LOG(x) std::cout +#define PLOG(x) std::cout +#define DATA_MNT_POINT "/data" + +using android::base::StringPrintf; +using android::base::WriteStringToFile; +using android::vold::kEmptyAuthentication; +using android::vold::KeyBuffer; + +// Store main DE raw ref / policy +std::string de_raw_ref; +// Map user ids to key references +std::map s_de_key_raw_refs; +std::map s_ce_key_raw_refs; +// TODO abolish this map, per b/26948053 +std::map s_ce_keys; + +namespace { + +struct PolicyKeyRef { + std::string contents_mode; + std::string filenames_mode; + std::string key_raw_ref; +}; + +const std::string device_key_dir = std::string() + DATA_MNT_POINT + e4crypt_unencrypted_folder; +const std::string device_key_path = device_key_dir + "/key"; +const std::string device_key_temp = device_key_dir + "/temp"; + +const std::string user_key_dir = std::string() + DATA_MNT_POINT + "/misc/vold/user_keys"; +const std::string user_key_temp = user_key_dir + "/temp"; +const std::string prepare_subdirs_path = "/system/bin/vold_prepare_subdirs"; + +const std::string systemwide_volume_key_dir = + std::string() + DATA_MNT_POINT + "/misc/vold/volume_keys"; + +bool s_global_de_initialized = false; + +// Some users are ephemeral, don't try to wipe their keys from disk +std::set s_ephemeral_users; + +} + +static bool e4crypt_is_emulated() { + return property_get_bool("persist.sys.emulate_fbe", false); +} + +static const char* escape_empty(const std::string& value) { + return value.empty() ? "null" : value.c_str(); +} + +static std::string get_de_key_path(userid_t user_id) { + return StringPrintf("%s/de/%d", user_key_dir.c_str(), user_id); +} + +static std::string get_ce_key_directory_path(userid_t user_id) { + return StringPrintf("%s/ce/%d", user_key_dir.c_str(), user_id); +} + +// Returns the keys newest first +static std::vector get_ce_key_paths(const std::string& directory_path) { + auto dirp = std::unique_ptr(opendir(directory_path.c_str()), closedir); + if (!dirp) { + PLOG(ERROR) << "Unable to open ce key directory: " + directory_path << std::endl; + return std::vector(); + } + std::vector result; + for (;;) { + errno = 0; + auto const entry = readdir(dirp.get()); + if (!entry) { + if (errno) { + PLOG(ERROR) << "Unable to read ce key directory: " + directory_path << std::endl; + return std::vector(); + } + break; + } + if (entry->d_type != DT_DIR || entry->d_name[0] != 'c') { + LOG(DEBUG) << "Skipping non-key " << entry->d_name << std::endl; + continue; + } + result.emplace_back(directory_path + "/" + entry->d_name); + } + std::sort(result.begin(), result.end()); + std::reverse(result.begin(), result.end()); + return result; +} + +static std::string get_ce_key_current_path(const std::string& directory_path) { + return directory_path + "/current"; +} + +/*static bool get_ce_key_new_path(const std::string& directory_path, + const std::vector& 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& paths) { + for (auto const other_path: paths) { + if (other_path != to_fix) { + android::vold::destroyKey(other_path); + } + } + auto const current_path = get_ce_key_current_path(directory_path); + if (to_fix != current_path) { + LOG(DEBUG) << "Renaming " << to_fix << " to " << current_path << std::endl; + if (rename(to_fix.c_str(), current_path.c_str()) != 0) { + PLOG(WARNING) << "Unable to rename " << to_fix << " to " << current_path << std::endl; + } + } +} + +static bool read_and_fixate_user_ce_key(userid_t user_id, + const android::vold::KeyAuthentication& auth, + KeyBuffer *ce_key) { + auto const directory_path = get_ce_key_directory_path(user_id); + auto const paths = get_ce_key_paths(directory_path); + for (auto const ce_key_path: paths) { + LOG(DEBUG) << "Trying user CE key " << ce_key_path << std::endl; + if (android::vold::retrieveKey(ce_key_path, auth, ce_key)) { + LOG(DEBUG) << "Successfully retrieved key" << std::endl; + fixate_user_ce_key(directory_path, ce_key_path, paths); + return true; + } + } + LOG(ERROR) << "Failed to find working ce key for user " << user_id << std::endl; + return false; +} + +static bool 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 << std::endl; + return true; +} + +static bool prepare_dir(const std::string& dir, mode_t mode, uid_t uid, gid_t gid) { + LOG(DEBUG) << "Preparing: " << dir << std::endl; + if (fs_prepare_dir(dir.c_str(), mode, uid, gid) != 0) { + PLOG(ERROR) << "Failed to prepare " << dir << std::endl; + return false; + } + return true; +} + +/*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; + } + std::string de_raw_ref; + if (!android::vold::installKey(de_key, &de_raw_ref)) return false; + s_de_key_raw_refs[user_id] = de_raw_ref; + std::string ce_raw_ref; + if (!android::vold::installKey(ce_key, &ce_raw_ref)) return false; + s_ce_keys[user_id] = ce_key; + s_ce_key_raw_refs[user_id] = ce_raw_ref; + LOG(DEBUG) << "Created keys for user " << user_id;*/ + LOG(DEBUG) << "TWRP not doing create_and_install_user_keys\n"; + return true; +} + +bool lookup_key_ref(const std::map& key_map, userid_t user_id, + std::string* raw_ref) { + auto refi = key_map.find(user_id); + if (refi == key_map.end()) { + LOG(ERROR) << "Cannot find key for " << user_id << std::endl; + return false; + } + *raw_ref = refi->second; + return true; +} + +static void get_data_file_encryption_modes(PolicyKeyRef* key_ref) { + /*struct fstab_rec* rec = fs_mgr_get_entry_for_mount_point(fstab_default, DATA_MNT_POINT); + char const* contents_mode = strdup("ice"); + char const* filenames_mode = strdup("aes-256-heh"); + fs_mgr_get_file_encryption_modes(rec, &contents_mode, &filenames_mode); + key_ref->contents_mode = contents_mode; + key_ref->filenames_mode = filenames_mode;*/ + LOG(INFO) << "contents mode '" << android::base::GetProperty("fbe.contents", "aes-256-xts") << "' filenames '" << android::base::GetProperty("fbe.filenames", "aes-256-heh") << "'\n"; + key_ref->contents_mode = + android::base::GetProperty("fbe.contents", "aes-256-xts"); + key_ref->filenames_mode = + android::base::GetProperty("fbe.filenames", "aes-256-heh"); +} + +static bool ensure_policy(const PolicyKeyRef& key_ref, const std::string& path) { + return e4crypt_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(opendir(de_dir.c_str()), closedir); + if (!dirp) { + PLOG(ERROR) << "Unable to read de key directory" << std::endl; + return false; + } + for (;;) { + errno = 0; + auto entry = readdir(dirp.get()); + if (!entry) { + if (errno) { + PLOG(ERROR) << "Unable to read de key directory" << std::endl; + return false; + } + break; + } + if (entry->d_type != DT_DIR || !is_numeric(entry->d_name)) { + LOG(DEBUG) << "Skipping non-de-key " << entry->d_name << std::endl; + continue; + } + userid_t user_id = std::stoi(entry->d_name); + if (s_de_key_raw_refs.count(user_id) == 0) { + auto key_path = de_dir + "/" + entry->d_name; + KeyBuffer key; + if (!android::vold::retrieveKey(key_path, kEmptyAuthentication, &key)) return false; + std::string raw_ref; + if (!android::vold::installKey(key, &raw_ref)) return false; + s_de_key_raw_refs[user_id] = raw_ref; + LOG(DEBUG) << "Installed de key for user " << user_id << std::endl; + } + } + // ext4enc:TODO: go through all DE directories, ensure that all user dirs have the + // correct policy set on them, and that no rogue ones exist. + return true; +} + +bool e4crypt_initialize_global_de() { + LOG(INFO) << "e4crypt_initialize_global_de" << std::endl; + + if (s_global_de_initialized) { + LOG(INFO) << "Already initialized" << std::endl; + return true; + } + + PolicyKeyRef device_ref; + LOG(INFO) << "calling retrieveAndInstallKey\n"; + 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") + e4crypt_key_mode; + if (!android::base::WriteStringToFile(modestring, mode_filename)) { + PLOG(ERROR) << "Cannot save type" << std::endl; + return false; + } + + std::string ref_filename = std::string("/data") + e4crypt_key_ref; + if (!android::base::WriteStringToFile(device_ref.key_raw_ref, ref_filename)) { + PLOG(ERROR) << "Cannot save key reference to:" << ref_filename << std::endl; + return false; + } + LOG(INFO) << "Wrote system DE key reference to:" << ref_filename << std::endl; + + s_global_de_initialized = true; + de_raw_ref = device_ref.key_raw_ref; + return true; +} + +bool e4crypt_init_user0() { + LOG(DEBUG) << "e4crypt_init_user0\n"; + if (e4crypt_is_native()) { + if (!prepare_dir(user_key_dir, 0700, AID_ROOT, AID_ROOT)) return false; + if (!prepare_dir(user_key_dir + "/ce", 0700, AID_ROOT, AID_ROOT)) return false; + if (!prepare_dir(user_key_dir + "/de", 0700, AID_ROOT, AID_ROOT)) return false; + if (!android::vold::pathExists(get_de_key_path(0))) { + if (!create_and_install_user_keys(0, false)) return false; + } + // TODO: switch to loading only DE_0 here once framework makes + // explicit calls to install DE keys for secondary users + if (!load_all_de_keys()) return false; + } + // We can only safely prepare DE storage here, since CE keys are probably + // entangled with user credentials. The framework will always prepare CE + // storage once CE keys are installed. + if (!e4crypt_prepare_user_storage("", 0, 0, /*android::os::IVold::*/STORAGE_FLAG_DE)) { + LOG(ERROR) << "Failed to prepare user 0 storage" << std::endl; + return false; + } + + // If this is a non-FBE device that recently left an emulated mode, + // restore user data directories to known-good state. + if (!e4crypt_is_native() && !e4crypt_is_emulated()) { + e4crypt_unlock_user_key(0, 0, "!", "!"); + } + + return true; +} + +/*bool e4crypt_vold_create_user_key(userid_t user_id, int serial, bool ephemeral) { + LOG(DEBUG) << "TWRP NOT e4crypt_vold_create_user_key for " << user_id << " serial " << serial; + return true; + if (!e4crypt_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 e4crypt_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 (!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) { + LOG(ERROR) << "TWRP NOT evict_ce_key\n"; + return true; + 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 e4crypt_destroy_user_key(userid_t user_id) { + LOG(DEBUG) << "NOT e4crypt_destroy_user_key(" << user_id << ")"; + return true; + if (!e4crypt_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 << std::endl; + // FIXME temporary workaround for b/26713622 + if (e4crypt_is_emulated()) return false; + } +#if EMULATED_USES_SELINUX + if (selinux_android_restorecon(path.c_str(), SELINUX_ANDROID_RESTORECON_FORCE) != 0) { + PLOG(WARNING) << "Failed to restorecon " << path << std::endl; + // FIXME temporary workaround for b/26713622 + if (e4crypt_is_emulated()) return false; + } +#endif + return true; +} + +static bool parse_hex(const std::string& hex, std::string* result) { + if (hex == "!") { + *result = ""; + return true; + } + if (android::vold::HexToStr(hex, *result) != 0) { + LOG(ERROR) << "Invalid FBE hex string" << std::endl; // Don't log the string for security reasons + return false; + } + return true; +} + +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 << std::endl; + 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 << std::endl; + 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 e4crypt_add_user_key_auth(userid_t user_id, int serial, const std::string& token_hex, + const std::string& secret_hex) { + LOG(DEBUG) << "e4crypt_add_user_key_auth " << user_id << " serial=" << serial + << " token_present=" << (token_hex != "!"); + if (!e4crypt_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; + return true; +} + +bool e4crypt_fixate_newest_user_key_auth(userid_t user_id) { + LOG(DEBUG) << "e4crypt_fixate_newest_user_key_auth " << user_id; + if (!e4crypt_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 e4crypt_unlock_user_key(userid_t user_id, int serial, const std::string& token_hex, + const std::string& secret_hex) { + LOG(DEBUG) << "e4crypt_unlock_user_key " << user_id << " serial=" << serial + << " token_present=" << (token_hex != "!") << std::endl; + if (e4crypt_is_native()) { + if (s_ce_key_raw_refs.count(user_id) != 0) { + LOG(WARNING) << "Tried to unlock already-unlocked key for user " << user_id << std::endl; + return true; + } + std::string token, secret; + if (!parse_hex(token_hex, &token)) return false; + if (!parse_hex(secret_hex, &secret)) return false; + android::vold::KeyAuthentication auth(token, secret); + if (!read_and_install_user_ce_key(user_id, auth)) { + LOG(ERROR) << "Couldn't read key for " << user_id << std::endl; + return false; + } + } else { + // When in emulation mode, we just use chmod. However, we also + // unlock directories when not in emulation mode, to bring devices + // back into a known-good state. + if (!emulated_unlock(android::vold::BuildDataSystemCePath(user_id), 0771) || + !emulated_unlock(android::vold::BuildDataMiscCePath(user_id), 01771) || + !emulated_unlock(android::vold::BuildDataMediaCePath("", user_id), 0770) || + !emulated_unlock(android::vold::BuildDataUserCePath("", user_id), 0771)) { + LOG(ERROR) << "Failed to unlock user " << user_id << std::endl; + return false; + } + } + return true; +} + +// TODO: rename to 'evict' for consistency +/*bool e4crypt_lock_user_key(userid_t user_id) { + LOG(DEBUG) << "TWRP NOTe4crypt_lock_user_key " << user_id; + return true; + if (e4crypt_is_native()) { + return evict_ce_key(user_id); + } else if (e4crypt_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) { + LOG(ERROR) << "not actually forking for vold_prepare_subdirs\n"; + return true; + /*if (0 != android::vold::ForkExecvp( + std::vector{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 e4crypt_prepare_user_storage(const std::string& volume_uuid, userid_t user_id, int serial, + int flags) { + LOG(DEBUG) << "e4crypt_prepare_user_storage for volume " << escape_empty(volume_uuid) + << ", user " << user_id << ", serial " << serial << ", flags " << flags << std::endl; + + if (flags & /*android::os::IVold::*/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(nullptr, 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 (e4crypt_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, nullptr, &de_ref)) return false; + } + if (!ensure_policy(de_ref, user_de_path)) return false; + } + } + + if (flags & /*android::os::IVold::*/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(nullptr, user_id); + auto user_ce_path = android::vold::BuildDataUserCePath(nullptr, 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 (e4crypt_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, nullptr, &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(misc_ce_path); + } + } + if (!prepare_subdirs("prepare", volume_uuid, user_id, flags)) return false; + return true; +} + +/*bool e4crypt_destroy_user_storage(const std::string& volume_uuid, userid_t user_id, int flags) { + LOG(DEBUG) << "TWRP NOT e4crypt_destroy_user_storage for volume " << escape_empty(volume_uuid) + << ", user " << user_id << ", flags " << flags; + bool res = true; + return res; + + res &= prepare_subdirs("destroy", volume_uuid, user_id, flags); + + if (flags & android::os::IVold::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(nullptr, user_id); + auto user_ce_path = android::vold::BuildDataUserCePath(nullptr, 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 (e4crypt_is_native()) { + res &= destroy_volkey(misc_ce_path, volume_uuid); + } + } + } + + if (flags & android::os::IVold::STORAGE_FLAG_DE) { + // DE_sys key + auto system_legacy_path = 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(nullptr, 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 (e4crypt_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) { + LOG(ERROR) << "TWRP NOT destroy_volume_keys\n"; + return true; + auto dirp = std::unique_ptr(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 e4crypt_destroy_volume_keys(const std::string& volume_uuid) { + bool res = true; + LOG(DEBUG) << "TWRP NOT e4crypt_destroy_volume_keys for volume " << escape_empty(volume_uuid); + /*return res; + 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/ext4crypt/Ext4CryptPie.h b/crypto/ext4crypt/Ext4CryptPie.h new file mode 100644 index 000000000..6e2afa534 --- /dev/null +++ b/crypto/ext4crypt/Ext4CryptPie.h @@ -0,0 +1,42 @@ +/* + * 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 +#include + +#include + +bool e4crypt_initialize_global_de(); + +bool e4crypt_init_user0(); +/*bool e4crypt_vold_create_user_key(userid_t user_id, int serial, bool ephemeral); +bool e4crypt_destroy_user_key(userid_t user_id); +bool e4crypt_add_user_key_auth(userid_t user_id, int serial, const std::string& token, + const std::string& secret); +bool e4crypt_fixate_newest_user_key_auth(userid_t user_id);*/ + +bool e4crypt_unlock_user_key(userid_t user_id, int serial, const std::string& token, + const std::string& secret); +//bool e4crypt_lock_user_key(userid_t user_id); + +bool e4crypt_prepare_user_storage(const std::string& volume_uuid, userid_t user_id, int serial, + int flags); +/*bool e4crypt_destroy_user_storage(const std::string& volume_uuid, userid_t user_id, int flags); + +bool e4crypt_destroy_volume_keys(const std::string& volume_uuid);*/ + +bool lookup_key_ref(const std::map& key_map, userid_t user_id, + std::string* raw_ref); diff --git a/crypto/ext4crypt/KeyBuffer.cpp b/crypto/ext4crypt/KeyBuffer.cpp new file mode 100644 index 000000000..e7aede532 --- /dev/null +++ b/crypto/ext4crypt/KeyBuffer.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "KeyBuffer.h" + +#include +#include + +namespace android { +namespace vold { + +KeyBuffer operator+(KeyBuffer&& lhs, const KeyBuffer& rhs) { + std::copy(rhs.begin(), rhs.end(), std::back_inserter(lhs)); + return std::move(lhs); +} + +KeyBuffer operator+(KeyBuffer&& lhs, const char* rhs) { + std::copy(rhs, rhs + strlen(rhs), std::back_inserter(lhs)); + return std::move(lhs); +} + +} // namespace vold +} // namespace android + diff --git a/crypto/ext4crypt/KeyBuffer.h b/crypto/ext4crypt/KeyBuffer.h new file mode 100644 index 000000000..2087187df --- /dev/null +++ b/crypto/ext4crypt/KeyBuffer.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_VOLD_KEYBUFFER_H +#define ANDROID_VOLD_KEYBUFFER_H + +#include +#include +#include + +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 { + public: + void deallocate(pointer p, size_type n) + { + memset_s(p, 0, n); + std::allocator::deallocate(p, n); + } +}; + +// Char vector that zeroes memory when deallocating. +using KeyBuffer = std::vector; + +// Convenience methods to concatenate key buffers. +KeyBuffer operator+(KeyBuffer&& lhs, const KeyBuffer& rhs); +KeyBuffer operator+(KeyBuffer&& lhs, const char* rhs); + +} // namespace vold +} // namespace android + +#endif + diff --git a/crypto/ext4crypt/KeyStorage4.cpp b/crypto/ext4crypt/KeyStorage4.cpp new file mode 100644 index 000000000..28efdf1a9 --- /dev/null +++ b/crypto/ext4crypt/KeyStorage4.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 "KeyStorage4.h" + +#include "Keymaster4.h" +#include "ScryptParameters.h" +#include "Utils.h" + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +//#include +#include + +#include + +#include +#include +#include + +#include +#define ERROR 1 +#define LOG(x) std::cout +#define PLOG(x) std::cout + +extern "C" { + +#include "crypto_scrypt.h" +} + +namespace android { +namespace vold { + +const KeyAuthentication kEmptyAuthentication{"", ""}; + +static constexpr size_t AES_KEY_BYTES = 32; +static constexpr size_t GCM_NONCE_BYTES = 12; +static constexpr size_t GCM_MAC_BYTES = 16; +static constexpr size_t SALT_BYTES = 1 << 4; +static constexpr size_t SECDISCARDABLE_BYTES = 1 << 14; +static constexpr size_t STRETCHED_BYTES = 1 << 6; + +static constexpr uint32_t AUTH_TIMEOUT = 30; // Seconds + +static const char* kCurrentVersion = "1"; +static const char* kRmPath = "/system/bin/rm"; +static const char* kSecdiscardPath = "/system/bin/secdiscard"; +static const char* kStretch_none = "none"; +static const char* kStretch_nopassword = "nopassword"; +static const std::string kStretchPrefix_scrypt = "scrypt "; +static const char* kHashPrefix_secdiscardable = "Android secdiscardable SHA512"; +static const char* kHashPrefix_keygen = "Android key wrapping key generation SHA512"; +static const char* kFn_encrypted_key = "encrypted_key"; +static const char* kFn_keymaster_key_blob = "keymaster_key_blob"; +static const char* kFn_keymaster_key_blob_upgraded = "keymaster_key_blob_upgraded"; +static const char* kFn_salt = "salt"; +static const char* kFn_secdiscardable = "secdiscardable"; +static const char* kFn_stretching = "stretching"; +static const char* kFn_version = "version"; + +static bool checkSize(const std::string& kind, size_t actual, size_t expected) { + if (actual != expected) { + LOG(ERROR) << "Wrong number of bytes in " << kind << ", expected " << expected << " got " + << actual << std::endl; + return false; + } + return true; +} + +static void hashWithPrefix(char const* prefix, const std::string& tohash, std::string* res) { + SHA512_CTX c; + + SHA512_Init(&c); + // Personalise the hashing by introducing a fixed prefix. + // Hashing applications should use personalization except when there is a + // specific reason not to; see section 4.11 of https://www.schneier.com/skein1.3.pdf + std::string hashingPrefix = prefix; + hashingPrefix.resize(SHA512_CBLOCK); + SHA512_Update(&c, hashingPrefix.data(), hashingPrefix.size()); + SHA512_Update(&c, tohash.data(), tohash.size()); + res->assign(SHA512_DIGEST_LENGTH, '\0'); + SHA512_Final(reinterpret_cast(&(*res)[0]), &c); +} + +static bool generateKeymasterKey(Keymaster& keymaster, const KeyAuthentication& auth, + const std::string& appId, std::string* key) { + auto paramBuilder = km::AuthorizationSetBuilder() + .AesEncryptionKey(AES_KEY_BYTES * 8) + .GcmModeMinMacLen(GCM_MAC_BYTES * 8) + .Authorization(km::TAG_APPLICATION_ID, km::support::blob2hidlVec(appId)); + if (auth.token.empty()) { + LOG(DEBUG) << "Creating key that doesn't need auth token" << std::endl; + paramBuilder.Authorization(km::TAG_NO_AUTH_REQUIRED); + } else { + LOG(DEBUG) << "Auth token required for key" << std::endl; + if (auth.token.size() != sizeof(hw_auth_token_t)) { + LOG(ERROR) << "Auth token should be " << sizeof(hw_auth_token_t) << " bytes, was " + << auth.token.size() << " bytes" << std::endl; + return false; + } + const hw_auth_token_t* at = reinterpret_cast(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 beginParams( + const KeyAuthentication& auth, const std::string& appId) { + auto paramBuilder = km::AuthorizationSetBuilder() + .GcmModeMacLen(GCM_MAC_BYTES * 8) + .Authorization(km::TAG_APPLICATION_ID, km::support::blob2hidlVec(appId)); + km::HardwareAuthToken authToken; + if (!auth.token.empty()) { + LOG(DEBUG) << "Supplying auth token to Keymaster" << std::endl; + authToken = km::support::hidlVec2AuthToken(km::support::blob2hidlVec(auth.token)); + } + return {paramBuilder, authToken}; +} + +static bool readFileToString(const std::string& filename, std::string* result) { + if (!android::base::ReadFileToString(filename, result)) { + PLOG(ERROR) << "Failed to read from " << filename << std::endl; + return false; + } + return true; +} + +static bool writeStringToFile(const std::string& payload, const std::string& filename) { + PLOG(ERROR) << __FUNCTION__ << " called for " << filename << " and being skipped\n"; + return true; + android::base::unique_fd fd(TEMP_FAILURE_RETRY( + open(filename.c_str(), O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC | O_CLOEXEC, 0666))); + if (fd == -1) { + PLOG(ERROR) << "Failed to open " << filename; + return false; + } + if (!android::base::WriteStringToFd(payload, fd)) { + PLOG(ERROR) << "Failed to write to " << filename; + unlink(filename.c_str()); + return false; + } + // fsync as close won't guarantee flush data + // see close(2), fsync(2) and b/68901441 + if (fsync(fd) == -1) { + if (errno == EROFS || errno == EINVAL) { + PLOG(WARNING) << "Skip fsync " << filename + << " on a file system does not support synchronization"; + } else { + PLOG(ERROR) << "Failed to fsync " << filename; + unlink(filename.c_str()); + return false; + } + } + return true; +} + +static bool readRandomBytesOrLog(size_t count, std::string* out) { + auto status = ReadRandomBytes(count, *out); + if (status != OK) { + LOG(ERROR) << "Random read failed with status: " << status << std::endl; + return false; + } + return true; +} + +bool createSecdiscardable(const std::string& filename, std::string* hash) { + std::string secdiscardable; + if (!readRandomBytesOrLog(SECDISCARDABLE_BYTES, &secdiscardable)) return false; + if (!writeStringToFile(secdiscardable, filename)) return false; + hashWithPrefix(kHashPrefix_secdiscardable, secdiscardable, hash); + return true; +} + +bool readSecdiscardable(const std::string& filename, std::string* hash) { + std::string secdiscardable; + if (!readFileToString(filename, &secdiscardable)) return false; + hashWithPrefix(kHashPrefix_secdiscardable, secdiscardable, hash); + return true; +} + +static KeymasterOperation begin(Keymaster& keymaster, const std::string& dir, + km::KeyPurpose purpose, const km::AuthorizationSet& keyParams, + const km::AuthorizationSet& opParams, + const km::HardwareAuthToken& authToken, + km::AuthorizationSet* outParams) { + auto kmKeyPath = dir + "/" + kFn_keymaster_key_blob; + std::string kmKey; + if (!readFileToString(kmKeyPath, &kmKey)) return KeymasterOperation(); + km::AuthorizationSet inParams(keyParams); + inParams.append(opParams.begin(), opParams.end()); + for (;;) { + auto opHandle = keymaster.begin(purpose, kmKey, inParams, authToken, outParams); + if (opHandle) { + return opHandle; + } + if (opHandle.errorCode() != km::ErrorCode::KEY_REQUIRES_UPGRADE) return opHandle; + LOG(DEBUG) << "Upgrading key in memory only: " << dir << std::endl; + std::string newKey; + if (!keymaster.upgradeKey(kmKey, keyParams, &newKey)) return KeymasterOperation(); + /*auto newKeyPath = dir + "/" + kFn_keymaster_key_blob_upgraded; + if (!writeStringToFile(newKey, newKeyPath)) return KeymasterOperation(); + if (rename(newKeyPath.c_str(), kmKeyPath.c_str()) != 0) { + PLOG(ERROR) << "Unable to move upgraded key to location: " << kmKeyPath; + return KeymasterOperation(); + } + if (!keymaster.deleteKey(kmKey)) { + LOG(ERROR) << "Key deletion failed during upgrade, continuing anyway: " << dir; + }*/ + kmKey = newKey; + LOG(INFO) << "Key upgraded in memory but not updated in folder: " << dir << std::endl; + } +} + +static bool encryptWithKeymasterKey(Keymaster& keymaster, const std::string& dir, + const km::AuthorizationSet& keyParams, + const km::HardwareAuthToken& authToken, + const KeyBuffer& message, std::string* ciphertext) { + km::AuthorizationSet opParams; + km::AuthorizationSet outParams; + auto opHandle = + begin(keymaster, dir, km::KeyPurpose::ENCRYPT, keyParams, opParams, authToken, &outParams); + if (!opHandle) return false; + auto nonceBlob = outParams.GetTagValue(km::TAG_NONCE); + if (!nonceBlob.isOk()) { + LOG(ERROR) << "GCM encryption but no nonce generated" << std::endl; + return false; + } + // nonceBlob here is just a pointer into existing data, must not be freed + std::string nonce(reinterpret_cast(&nonceBlob.value()[0]), + nonceBlob.value().size()); + if (!checkSize("nonce", nonce.size(), GCM_NONCE_BYTES)) return false; + std::string body; + if (!opHandle.updateCompletely(message, &body)) return false; + + std::string mac; + if (!opHandle.finish(&mac)) return false; + if (!checkSize("mac", mac.size(), GCM_MAC_BYTES)) return false; + *ciphertext = nonce + body + mac; + return true; +} + +static bool decryptWithKeymasterKey(Keymaster& keymaster, const std::string& dir, + const km::AuthorizationSet& keyParams, + const km::HardwareAuthToken& authToken, + const std::string& ciphertext, KeyBuffer* message) { + auto nonce = ciphertext.substr(0, GCM_NONCE_BYTES); + auto bodyAndMac = ciphertext.substr(GCM_NONCE_BYTES); + auto opParams = km::AuthorizationSetBuilder().Authorization(km::TAG_NONCE, + km::support::blob2hidlVec(nonce)); + auto opHandle = + begin(keymaster, dir, km::KeyPurpose::DECRYPT, keyParams, opParams, authToken, nullptr); + if (!opHandle) return false; + if (!opHandle.updateCompletely(bodyAndMac, message)) return false; + if (!opHandle.finish(nullptr)) return false; + return true; +} + +static std::string getStretching(const KeyAuthentication& auth) { + if (!auth.usesKeymaster()) { + return kStretch_none; + } else if (auth.secret.empty()) { + return kStretch_nopassword; + } else { + char paramstr[PROPERTY_VALUE_MAX]; + + property_get(SCRYPT_PROP, paramstr, SCRYPT_DEFAULTS); + return std::string() + kStretchPrefix_scrypt + paramstr; + } +} + +static bool stretchingNeedsSalt(const std::string& stretching) { + return stretching != kStretch_nopassword && stretching != kStretch_none; +} + +static bool stretchSecret(const std::string& stretching, const std::string& secret, + const std::string& salt, std::string* stretched) { + if (stretching == kStretch_nopassword) { + if (!secret.empty()) { + LOG(WARNING) << "Password present but stretching is nopassword" << std::endl; + // Continue anyway + } + stretched->clear(); + } else if (stretching == kStretch_none) { + *stretched = secret; + } else if (std::equal(kStretchPrefix_scrypt.begin(), kStretchPrefix_scrypt.end(), + stretching.begin())) { + int Nf, rf, pf; + if (!parse_scrypt_parameters(stretching.substr(kStretchPrefix_scrypt.size()).c_str(), &Nf, + &rf, &pf)) { + LOG(ERROR) << "Unable to parse scrypt params in stretching: " << stretching << std::endl; + return false; + } + stretched->assign(STRETCHED_BYTES, '\0'); + if (crypto_scrypt(reinterpret_cast(secret.data()), secret.size(), + reinterpret_cast(salt.data()), salt.size(), 1 << Nf, + 1 << rf, 1 << pf, reinterpret_cast(&(*stretched)[0]), + stretched->size()) != 0) { + LOG(ERROR) << "scrypt failed with params: " << stretching << std::endl; + return false; + } + } else { + LOG(ERROR) << "Unknown stretching type: " << stretching << std::endl; + return false; + } + return true; +} + +static bool generateAppId(const KeyAuthentication& auth, const std::string& stretching, + const std::string& salt, const std::string& secdiscardable_hash, + std::string* appId) { + std::string stretched; + if (!stretchSecret(stretching, auth.secret, salt, &stretched)) return false; + *appId = secdiscardable_hash + stretched; + return true; +} + +static void logOpensslError() { + LOG(ERROR) << "Openssl error: " << ERR_get_error() << std::endl; +} + +static bool encryptWithoutKeymaster(const std::string& preKey, const KeyBuffer& plaintext, + std::string* ciphertext) { + std::string key; + hashWithPrefix(kHashPrefix_keygen, preKey, &key); + key.resize(AES_KEY_BYTES); + if (!readRandomBytesOrLog(GCM_NONCE_BYTES, ciphertext)) return false; + auto ctx = std::unique_ptr( + EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free); + if (!ctx) { + logOpensslError(); + return false; + } + if (1 != EVP_EncryptInit_ex(ctx.get(), EVP_aes_256_gcm(), NULL, + reinterpret_cast(key.data()), + reinterpret_cast(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(&(*ciphertext)[0] + GCM_NONCE_BYTES), + &outlen, reinterpret_cast(plaintext.data()), plaintext.size())) { + logOpensslError(); + return false; + } + if (outlen != static_cast(plaintext.size())) { + LOG(ERROR) << "GCM ciphertext length should be " << plaintext.size() << " was " << outlen << std::endl; + return false; + } + if (1 != EVP_EncryptFinal_ex( + ctx.get(), + reinterpret_cast(&(*ciphertext)[0] + GCM_NONCE_BYTES + plaintext.size()), + &outlen)) { + logOpensslError(); + return false; + } + if (outlen != 0) { + LOG(ERROR) << "GCM EncryptFinal should be 0, was " << outlen << std::endl; + return false; + } + if (1 != EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_GET_TAG, GCM_MAC_BYTES, + reinterpret_cast(&(*ciphertext)[0] + GCM_NONCE_BYTES + + plaintext.size()))) { + logOpensslError(); + return false; + } + return true; +} + +static bool decryptWithoutKeymaster(const std::string& preKey, const std::string& ciphertext, + KeyBuffer* plaintext) { + if (ciphertext.size() < GCM_NONCE_BYTES + GCM_MAC_BYTES) { + LOG(ERROR) << "GCM ciphertext too small: " << ciphertext.size() << std::endl; + return false; + } + std::string key; + hashWithPrefix(kHashPrefix_keygen, preKey, &key); + key.resize(AES_KEY_BYTES); + auto ctx = std::unique_ptr( + EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free); + if (!ctx) { + logOpensslError(); + return false; + } + if (1 != EVP_DecryptInit_ex(ctx.get(), EVP_aes_256_gcm(), NULL, + reinterpret_cast(key.data()), + reinterpret_cast(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(&(*plaintext)[0]), &outlen, + reinterpret_cast(ciphertext.data() + GCM_NONCE_BYTES), + plaintext->size())) { + logOpensslError(); + return false; + } + if (outlen != static_cast(plaintext->size())) { + LOG(ERROR) << "GCM plaintext length should be " << plaintext->size() << " was " << outlen << std::endl; + return false; + } + if (1 != EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_SET_TAG, GCM_MAC_BYTES, + const_cast(reinterpret_cast( + ciphertext.data() + GCM_NONCE_BYTES + plaintext->size())))) { + logOpensslError(); + return false; + } + if (1 != EVP_DecryptFinal_ex(ctx.get(), + reinterpret_cast(&(*plaintext)[0] + plaintext->size()), + &outlen)) { + logOpensslError(); + return false; + } + if (outlen != 0) { + LOG(ERROR) << "GCM EncryptFinal should be 0, was " << outlen << std::endl; + return false; + } + return true; +} + +bool pathExists(const std::string& path) { + return access(path.c_str(), F_OK) == 0; +} + +bool storeKey(const std::string& dir, const KeyAuthentication& auth, const KeyBuffer& key) { + if (TEMP_FAILURE_RETRY(mkdir(dir.c_str(), 0700)) == -1) { + PLOG(ERROR) << "key mkdir " << dir << std::endl; + return false; + } + if (!writeStringToFile(kCurrentVersion, dir + "/" + kFn_version)) return false; + std::string secdiscardable_hash; + if (!createSecdiscardable(dir + "/" + kFn_secdiscardable, &secdiscardable_hash)) return false; + std::string stretching = getStretching(auth); + if (!writeStringToFile(stretching, dir + "/" + kFn_stretching)) return false; + std::string salt; + if (stretchingNeedsSalt(stretching)) { + if (ReadRandomBytes(SALT_BYTES, salt) != OK) { + LOG(ERROR) << "Random read failed" << std::endl; + return false; + } + if (!writeStringToFile(salt, dir + "/" + kFn_salt)) return false; + } + std::string appId; + if (!generateAppId(auth, stretching, salt, secdiscardable_hash, &appId)) return false; + std::string encryptedKey; + if (auth.usesKeymaster()) { + Keymaster keymaster; + if (!keymaster) return false; + std::string kmKey; + if (!generateKeymasterKey(keymaster, auth, appId, &kmKey)) return false; + if (!writeStringToFile(kmKey, dir + "/" + kFn_keymaster_key_blob)) return false; + km::AuthorizationSet keyParams; + km::HardwareAuthToken authToken; + std::tie(keyParams, authToken) = beginParams(auth, appId); + if (!encryptWithKeymasterKey(keymaster, dir, keyParams, authToken, key, &encryptedKey)) + return false; + } else { + if (!encryptWithoutKeymaster(appId, key, &encryptedKey)) return false; + } + if (!writeStringToFile(encryptedKey, dir + "/" + kFn_encrypted_key)) return false; + return true; +} + +bool storeKeyAtomically(const std::string& key_path, const std::string& tmp_path, + const KeyAuthentication& auth, const KeyBuffer& key) { + if (pathExists(key_path)) { + LOG(ERROR) << "Already exists, cannot create key at: " << key_path << std::endl; + return false; + } + if (pathExists(tmp_path)) { + LOG(DEBUG) << "Already exists, destroying: " << tmp_path << std::endl; + destroyKey(tmp_path); // May be partially created so ignore errors + } + if (!storeKey(tmp_path, auth, key)) return false; + if (rename(tmp_path.c_str(), key_path.c_str()) != 0) { + PLOG(ERROR) << "Unable to move new key to location: " << key_path << std::endl; + return false; + } + LOG(DEBUG) << "Created key: " << key_path << std::endl; + return true; +} + +bool retrieveKey(const std::string& dir, const KeyAuthentication& auth, KeyBuffer* key) { + std::string version; + if (!readFileToString(dir + "/" + kFn_version, &version)) return false; + if (version != kCurrentVersion) { + LOG(ERROR) << "Version mismatch, expected " << kCurrentVersion << " got " << version << std::endl; + return false; + } + std::string secdiscardable_hash; + if (!readSecdiscardable(dir + "/" + kFn_secdiscardable, &secdiscardable_hash)) return false; + std::string stretching; + if (!readFileToString(dir + "/" + kFn_stretching, &stretching)) return false; + std::string salt; + if (stretchingNeedsSalt(stretching)) { + if (!readFileToString(dir + "/" + kFn_salt, &salt)) return false; + } + std::string appId; + if (!generateAppId(auth, stretching, salt, secdiscardable_hash, &appId)) return false; + std::string encryptedMessage; + if (!readFileToString(dir + "/" + kFn_encrypted_key, &encryptedMessage)) return false; + if (auth.usesKeymaster()) { + Keymaster keymaster; + if (!keymaster) return false; + km::AuthorizationSet keyParams; + km::HardwareAuthToken authToken; + std::tie(keyParams, authToken) = beginParams(auth, appId); + if (!decryptWithKeymasterKey(keymaster, dir, keyParams, authToken, encryptedMessage, key)) + return false; + } else { + if (!decryptWithoutKeymaster(appId, encryptedMessage, key)) return false; + } + return true; +} + +static bool deleteKey(const std::string& dir) { + LOG(DEBUG) << "not deleting key in " << __FILE__ << std::endl; + return true; + std::string kmKey; + if (!readFileToString(dir + "/" + kFn_keymaster_key_blob, &kmKey)) return false; + Keymaster keymaster; + if (!keymaster) return false; + if (!keymaster.deleteKey(kmKey)) return false; + return true; +} + +bool runSecdiscardSingle(const std::string& file) { + if (ForkExecvp(std::vector{kSecdiscardPath, "--", file}) != 0) { + LOG(ERROR) << "secdiscard failed" << std::endl; + return false; + } + return true; +} + +static bool recursiveDeleteKey(const std::string& dir) { + LOG(DEBUG) << "not recursively deleting key in " << __FILE__ << std::endl; + return true; + if (ForkExecvp(std::vector{kRmPath, "-rf", dir}) != 0) { + LOG(ERROR) << "recursive delete failed" << std::endl; + return false; + } + return true; +} + +bool destroyKey(const std::string& dir) { + LOG(DEBUG) << "not destroying key in " << __FILE__ << std::endl; + return true; + bool success = true; + // Try each thing, even if previous things failed. + bool uses_km = pathExists(dir + "/" + kFn_keymaster_key_blob); + if (uses_km) { + success &= deleteKey(dir); + } + auto secdiscard_cmd = std::vector{ + kSecdiscardPath, "--", dir + "/" + kFn_encrypted_key, dir + "/" + kFn_secdiscardable, + }; + if (uses_km) { + secdiscard_cmd.emplace_back(dir + "/" + kFn_keymaster_key_blob); + } + if (ForkExecvp(secdiscard_cmd) != 0) { + LOG(ERROR) << "secdiscard failed" << std::endl; + success = false; + } + success &= recursiveDeleteKey(dir); + return success; +} + +} // namespace vold +} // namespace android diff --git a/crypto/ext4crypt/KeyStorage4.h b/crypto/ext4crypt/KeyStorage4.h new file mode 100644 index 000000000..35ff65e63 --- /dev/null +++ b/crypto/ext4crypt/KeyStorage4.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_TWRP_KEYSTORAGE_H +#define ANDROID_TWRP_KEYSTORAGE_H + +#include "KeyBuffer.h" + +#include + +namespace android { +namespace vold { + +// Represents the information needed to decrypt a disk encryption key. +// If "token" is nonempty, it is passed in as a required Gatekeeper auth token. +// If "token" and "secret" are nonempty, "secret" is appended to the application-specific +// binary needed to unlock. +// If only "secret" is nonempty, it is used to decrypt in a non-Keymaster process. +class KeyAuthentication { + public: + KeyAuthentication(std::string t, std::string s) : token{t}, secret{s} {}; + + bool usesKeymaster() const { return !token.empty() || secret.empty(); }; + + const std::string token; + const std::string secret; +}; + +extern const KeyAuthentication kEmptyAuthentication; + +// Checks if path "path" exists. +bool pathExists(const std::string& path); + +bool createSecdiscardable(const std::string& path, std::string* hash); +bool readSecdiscardable(const std::string& path, std::string* hash); + +// Create a directory at the named path, and store "key" in it, +// in such a way that it can only be retrieved via Keymaster and +// can be securely deleted. +// It's safe to move/rename the directory after creation. +bool storeKey(const std::string& dir, const KeyAuthentication& auth, const KeyBuffer& key); + +// Create a directory at the named path, and store "key" in it as storeKey +// This version creates the key in "tmp_path" then atomically renames "tmp_path" +// to "key_path" thereby ensuring that the key is either stored entirely or +// not at all. +bool storeKeyAtomically(const std::string& key_path, const std::string& tmp_path, + const KeyAuthentication& auth, const KeyBuffer& key); + +// Retrieve the key from the named directory. +bool retrieveKey(const std::string& dir, const KeyAuthentication& auth, KeyBuffer* key); + +// Securely destroy the key stored in the named directory and delete the directory. +bool destroyKey(const std::string& dir); + +bool runSecdiscardSingle(const std::string& file); +} // namespace vold +} // namespace android + +#endif diff --git a/crypto/ext4crypt/KeyUtil.cpp b/crypto/ext4crypt/KeyUtil.cpp new file mode 100644 index 000000000..946c6cf9c --- /dev/null +++ b/crypto/ext4crypt/KeyUtil.cpp @@ -0,0 +1,215 @@ +/* + * 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 +#include +#include + +#include + +#include +//#include +#include + +#include "KeyStorage4.h" +#include "Utils.h" + +#include +#define LOG(x) std::cout +#define PLOG(x) std::cout +#include +#include + +namespace android { +namespace vold { + +// ext4enc:TODO get this const from somewhere good +const int EXT4_KEY_DESCRIPTOR_SIZE = 8; + +// ext4enc:TODO Include structure from somewhere sensible +// MUST be in sync with ext4_crypto.c in kernel +constexpr int EXT4_ENCRYPTION_MODE_AES_256_XTS = 1; +constexpr int EXT4_AES_256_XTS_KEY_SIZE = 64; +constexpr int EXT4_MAX_KEY_SIZE = 64; +struct ext4_encryption_key { + uint32_t mode; + char raw[EXT4_MAX_KEY_SIZE]; + uint32_t size; +}; + +bool randomKey(KeyBuffer* key) { + *key = KeyBuffer(EXT4_AES_256_XTS_KEY_SIZE); + if (ReadRandomBytes(key->size(), key->data()) != 0) { + // TODO status_t plays badly with PLOG, fix it. + LOG(ERROR) << "Random read failed" << std::endl; + return false; + } + return true; +} + +// Get raw keyref - used to make keyname and to pass to ioctl +static std::string generateKeyRef(const char* key, int length) { + SHA512_CTX c; + + SHA512_Init(&c); + SHA512_Update(&c, key, length); + unsigned char key_ref1[SHA512_DIGEST_LENGTH]; + SHA512_Final(key_ref1, &c); + + SHA512_Init(&c); + SHA512_Update(&c, key_ref1, SHA512_DIGEST_LENGTH); + unsigned char key_ref2[SHA512_DIGEST_LENGTH]; + SHA512_Final(key_ref2, &c); + + static_assert(EXT4_KEY_DESCRIPTOR_SIZE <= SHA512_DIGEST_LENGTH, + "Hash too short for descriptor"); + return std::string((char*)key_ref2, EXT4_KEY_DESCRIPTOR_SIZE); +} + +static bool fillKey(const KeyBuffer& key, ext4_encryption_key* ext4_key) { + if (key.size() != EXT4_AES_256_XTS_KEY_SIZE) { + LOG(ERROR) << "Wrong size key " << key.size(); + return false; + } + static_assert(EXT4_AES_256_XTS_KEY_SIZE <= sizeof(ext4_key->raw), "Key too long!"); + ext4_key->mode = EXT4_ENCRYPTION_MODE_AES_256_XTS; + ext4_key->size = key.size(); + memset(ext4_key->raw, 0, sizeof(ext4_key->raw)); + memcpy(ext4_key->raw, key.data(), key.size()); + return true; +} + +static char const* const NAME_PREFIXES[] = { + "ext4", + "f2fs", + "fscrypt", + nullptr +}; + +static std::string keyname(const std::string& prefix, const std::string& raw_ref) { + std::ostringstream o; + o << prefix << ":"; + for (unsigned char i : raw_ref) { + o << std::hex << std::setw(2) << std::setfill('0') << (int)i; + } + return o.str(); +} + +// Get the keyring we store all keys in +static bool e4cryptKeyring(key_serial_t* device_keyring) { + *device_keyring = keyctl_search(KEY_SPEC_SESSION_KEYRING, "keyring", "e4crypt", 0); + if (*device_keyring == -1) { + PLOG(ERROR) << "Unable to find device keyring" << std::endl; + return false; + } + return true; +} + +// Install password into global keyring +// Return raw key reference for use in policy +bool installKey(const KeyBuffer& key, std::string* raw_ref) { + // Place ext4_encryption_key into automatically zeroing buffer. + KeyBuffer ext4KeyBuffer(sizeof(ext4_encryption_key)); + ext4_encryption_key &ext4_key = *reinterpret_cast(ext4KeyBuffer.data()); + + if (!fillKey(key, &ext4_key)) return false; + *raw_ref = generateKeyRef(ext4_key.raw, ext4_key.size); + key_serial_t device_keyring; + if (!e4cryptKeyring(&device_keyring)) return false; + for (char const* const* name_prefix = NAME_PREFIXES; *name_prefix != nullptr; name_prefix++) { + auto ref = keyname(*name_prefix, *raw_ref); + key_serial_t key_id = + add_key("logon", ref.c_str(), (void*)&ext4_key, sizeof(ext4_key), device_keyring); + if (key_id == -1) { + PLOG(ERROR) << "Failed to insert key into keyring " << device_keyring << std::endl; + return false; + } + LOG(DEBUG) << "Added key " << key_id << " (" << ref << ") to keyring " << device_keyring + << " in process " << getpid() << std::endl; + } + return true; +} + +bool evictKey(const std::string& raw_ref) { + LOG(ERROR) << "not actually evicting key\n"; + return true; + key_serial_t device_keyring; + if (!e4cryptKeyring(&device_keyring)) return false; + bool success = true; + for (char const* const* name_prefix = NAME_PREFIXES; *name_prefix != nullptr; name_prefix++) { + auto ref = keyname(*name_prefix, raw_ref); + auto key_serial = keyctl_search(device_keyring, "logon", ref.c_str(), 0); + + // Unlink the key from the keyring. Prefer unlinking to revoking or + // invalidating, since unlinking is actually no less secure currently, and + // it avoids bugs in certain kernel versions where the keyring key is + // referenced from places it shouldn't be. + if (keyctl_unlink(key_serial, device_keyring) != 0) { + PLOG(ERROR) << "Failed to unlink key with serial " << key_serial << " ref " << ref; + success = false; + } else { + LOG(DEBUG) << "Unlinked key with serial " << key_serial << " ref " << ref; + } + } + return success; +} + +bool retrieveAndInstallKey(bool create_if_absent, const KeyAuthentication& key_authentication, + const std::string& key_path, const std::string& tmp_path, + std::string* key_ref) { + KeyBuffer key; + if (pathExists(key_path)) { + LOG(DEBUG) << "Key exists, using: " << key_path << std::endl; + if (!retrieveKey(key_path, key_authentication, &key)) return false; + } else { + if (!create_if_absent) { + LOG(ERROR) << "No key found in " << key_path << std::endl; + return false; + } + LOG(INFO) << "Creating new key in " << key_path << std::endl; + if (!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 << std::endl; + return false; + } + return true; +} + +bool retrieveKey(bool create_if_absent, const std::string& key_path, + const std::string& tmp_path, KeyBuffer* key) { + if (pathExists(key_path)) { + LOG(DEBUG) << "Key exists, using: " << key_path << std::endl; + if (!retrieveKey(key_path, kEmptyAuthentication, key)) return false; + } else { + if (!create_if_absent) { + LOG(ERROR) << "No key found in " << key_path << std::endl; + return false; + } + LOG(INFO) << "Creating new key in " << key_path << std::endl; + if (!randomKey(key)) return false; + if (!storeKeyAtomically(key_path, tmp_path, + kEmptyAuthentication, *key)) return false; + } + return true; +} + +} // namespace vold +} // namespace android diff --git a/crypto/ext4crypt/KeyUtil.h b/crypto/ext4crypt/KeyUtil.h new file mode 100644 index 000000000..d5ad69fdb --- /dev/null +++ b/crypto/ext4crypt/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 "KeyStorage4.h" + +#include +#include + +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); + +} // namespace vold +} // namespace android + +#endif diff --git a/crypto/ext4crypt/Keymaster4.cpp b/crypto/ext4crypt/Keymaster4.cpp new file mode 100644 index 000000000..e25d0c45d --- /dev/null +++ b/crypto/ext4crypt/Keymaster4.cpp @@ -0,0 +1,352 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Keymaster4.h" + +//#include +#include +#include + +#include +#define LOG(x) std::cout +#define PLOG(x) std::cout + +namespace android { +namespace vold { + +using ::android::hardware::hidl_string; +using ::android::hardware::hidl_vec; +using ::android::hardware::keymaster::V4_0::SecurityLevel; + +KeymasterOperation::~KeymasterOperation() { + if (mDevice) mDevice->abort(mOpHandle); +} + +bool KeymasterOperation::updateCompletely(const char* input, size_t inputLen, + const std::function consumer) { + uint32_t inputConsumed = 0; + + km::ErrorCode km_error; + auto hidlCB = [&](km::ErrorCode ret, uint32_t inputConsumedDelta, + const hidl_vec& /*ignored*/, + const hidl_vec& _output) { + km_error = ret; + if (km_error != km::ErrorCode::OK) return; + inputConsumed += inputConsumedDelta; + consumer(reinterpret_cast(&_output[0]), _output.size()); + }; + + while (inputConsumed != inputLen) { + size_t toRead = static_cast(inputLen - inputConsumed); + auto inputBlob = km::support::blob2hidlVec( + reinterpret_cast(&input[inputConsumed]), toRead); + auto error = mDevice->update(mOpHandle, hidl_vec(), inputBlob, + km::HardwareAuthToken(), km::VerificationToken(), hidlCB); + if (!error.isOk()) { + LOG(ERROR) << "update failed: " << error.description() << std::endl; + mDevice = nullptr; + return false; + } + if (km_error != km::ErrorCode::OK) { + LOG(ERROR) << "update failed, code " << int32_t(km_error) << std::endl; + mDevice = nullptr; + return false; + } + if (inputConsumed > inputLen) { + LOG(ERROR) << "update reported too much input consumed" << std::endl; + mDevice = nullptr; + return false; + } + } + return true; +} + +bool KeymasterOperation::finish(std::string* output) { + km::ErrorCode km_error; + auto hidlCb = [&](km::ErrorCode ret, const hidl_vec& /*ignored*/, + const hidl_vec& _output) { + km_error = ret; + if (km_error != km::ErrorCode::OK) return; + if (output) output->assign(reinterpret_cast(&_output[0]), _output.size()); + }; + auto error = mDevice->finish(mOpHandle, hidl_vec(), hidl_vec(), + hidl_vec(), km::HardwareAuthToken(), + km::VerificationToken(), hidlCb); + mDevice = nullptr; + if (!error.isOk()) { + LOG(ERROR) << "finish failed: " << error.description() << std::endl; + return false; + } + if (km_error != km::ErrorCode::OK) { + LOG(ERROR) << "finish failed, code " << int32_t(km_error) << std::endl; + return false; + } + return true; +} + +/* static */ bool Keymaster::hmacKeyGenerated = false; + +Keymaster::Keymaster() { + auto devices = KmDevice::enumerateAvailableDevices(); + if (!hmacKeyGenerated) { + KmDevice::performHmacKeyAgreement(devices); + hmacKeyGenerated = true; + } + for (auto& dev : devices) { + // Do not use StrongBox for device encryption / credential encryption. If a security chip + // is present it will have Weaver, which already strengthens CE. We get no additional + // benefit from using StrongBox here, so skip it. + if (dev->halVersion().securityLevel != SecurityLevel::STRONGBOX) { + mDevice = std::move(dev); + break; + } + } + if (!mDevice) return; + auto& version = mDevice->halVersion(); + LOG(INFO) << "Using " << version.keymasterName << " from " << version.authorName + << " for encryption. Security level: " << toString(version.securityLevel) + << ", HAL: " << mDevice->descriptor() << "/" << mDevice->instanceName() << std::endl; +} + +bool Keymaster::generateKey(const km::AuthorizationSet& inParams, std::string* key) { + km::ErrorCode km_error; + auto hidlCb = [&](km::ErrorCode ret, const hidl_vec& keyBlob, + const km::KeyCharacteristics& /*ignored*/) { + km_error = ret; + if (km_error != km::ErrorCode::OK) return; + if (key) key->assign(reinterpret_cast(&keyBlob[0]), keyBlob.size()); + }; + + auto error = mDevice->generateKey(inParams.hidl_data(), hidlCb); + if (!error.isOk()) { + LOG(ERROR) << "generate_key failed: " << error.description() << std::endl; + return false; + } + if (km_error != km::ErrorCode::OK) { + LOG(ERROR) << "generate_key failed, code " << int32_t(km_error) << std::endl; + return false; + } + return true; +} + +bool Keymaster::deleteKey(const std::string& key) { + LOG(ERROR) << "not actually deleting key\n"; + return true; + auto keyBlob = km::support::blob2hidlVec(key); + auto error = mDevice->deleteKey(keyBlob); + if (!error.isOk()) { + LOG(ERROR) << "delete_key failed: " << error.description(); + return false; + } + if (error != km::ErrorCode::OK) { + LOG(ERROR) << "delete_key failed, code " << int32_t(km::ErrorCode(error)); + return false; + } + return true; +} + +bool Keymaster::upgradeKey(const std::string& oldKey, const km::AuthorizationSet& inParams, + std::string* newKey) { + auto oldKeyBlob = km::support::blob2hidlVec(oldKey); + km::ErrorCode km_error; + auto hidlCb = [&](km::ErrorCode ret, const hidl_vec& upgradedKeyBlob) { + km_error = ret; + if (km_error != km::ErrorCode::OK) return; + if (newKey) + newKey->assign(reinterpret_cast(&upgradedKeyBlob[0]), + upgradedKeyBlob.size()); + }; + auto error = mDevice->upgradeKey(oldKeyBlob, inParams.hidl_data(), hidlCb); + if (!error.isOk()) { + LOG(ERROR) << "upgrade_key failed: " << error.description() << std::endl; + return false; + } + if (km_error != km::ErrorCode::OK) { + LOG(ERROR) << "upgrade_key failed, code " << int32_t(km_error) << std::endl; + return false; + } + return true; +} + +KeymasterOperation Keymaster::begin(km::KeyPurpose purpose, const std::string& key, + const km::AuthorizationSet& inParams, + const km::HardwareAuthToken& authToken, + km::AuthorizationSet* outParams) { + auto keyBlob = km::support::blob2hidlVec(key); + uint64_t mOpHandle; + km::ErrorCode km_error; + + auto hidlCb = [&](km::ErrorCode ret, const hidl_vec& _outParams, + uint64_t operationHandle) { + km_error = ret; + if (km_error != km::ErrorCode::OK) return; + if (outParams) *outParams = _outParams; + mOpHandle = operationHandle; + }; + + auto error = mDevice->begin(purpose, keyBlob, inParams.hidl_data(), authToken, hidlCb); + if (!error.isOk()) { + LOG(ERROR) << "begin failed: " << error.description() << std::endl; + return KeymasterOperation(km::ErrorCode::UNKNOWN_ERROR); + } + if (km_error != km::ErrorCode::OK) { + LOG(ERROR) << "begin failed, code " << int32_t(km_error) << std::endl; + return KeymasterOperation(km_error); + } + return KeymasterOperation(mDevice.get(), mOpHandle); +} + +bool Keymaster::isSecure() { + return mDevice->halVersion().securityLevel != km::SecurityLevel::SOFTWARE; +} + +} // namespace vold +} // namespace android + +using namespace ::android::vold; + +int keymaster_compatibility_cryptfs_scrypt() { + Keymaster dev; + if (!dev) { + LOG(ERROR) << "Failed to initiate keymaster session" << std::endl; + return -1; + } + return dev.isSecure(); +} + +static bool write_string_to_buf(const std::string& towrite, uint8_t* buffer, uint32_t buffer_size, + uint32_t* out_size) { + if (!buffer || !out_size) { + LOG(ERROR) << "Missing target pointers" << std::endl; + return false; + } + *out_size = towrite.size(); + if (buffer_size < towrite.size()) { + LOG(ERROR) << "Buffer too small " << buffer_size << " < " << towrite.size() << std::endl; + return false; + } + memset(buffer, '\0', buffer_size); + std::copy(towrite.begin(), towrite.end(), buffer); + return true; +} + +static km::AuthorizationSet keyParams(uint32_t rsa_key_size, uint64_t rsa_exponent, + uint32_t ratelimit) { + return km::AuthorizationSetBuilder() + .RsaSigningKey(rsa_key_size, rsa_exponent) + .NoDigestOrPadding() + .Authorization(km::TAG_BLOB_USAGE_REQUIREMENTS, km::KeyBlobUsageRequirements::STANDALONE) + .Authorization(km::TAG_NO_AUTH_REQUIRED) + .Authorization(km::TAG_MIN_SECONDS_BETWEEN_OPS, ratelimit); +} + +int keymaster_create_key_for_cryptfs_scrypt(uint32_t rsa_key_size, uint64_t rsa_exponent, + uint32_t ratelimit, uint8_t* key_buffer, + uint32_t key_buffer_size, uint32_t* key_out_size) { + if (key_out_size) { + *key_out_size = 0; + } + Keymaster dev; + if (!dev) { + LOG(ERROR) << "Failed to initiate keymaster session" << std::endl; + return -1; + } + std::string key; + if (!dev.generateKey(keyParams(rsa_key_size, rsa_exponent, ratelimit), &key)) return -1; + if (!write_string_to_buf(key, key_buffer, key_buffer_size, key_out_size)) return -1; + return 0; +} + +int keymaster_upgrade_key_for_cryptfs_scrypt(uint32_t rsa_key_size, uint64_t rsa_exponent, + uint32_t ratelimit, const uint8_t* key_blob, + size_t key_blob_size, uint8_t* key_buffer, + uint32_t key_buffer_size, uint32_t* key_out_size) { + if (key_out_size) { + *key_out_size = 0; + } + Keymaster dev; + if (!dev) { + LOG(ERROR) << "Failed to initiate keymaster session" << std::endl; + return -1; + } + std::string old_key(reinterpret_cast(key_blob), key_blob_size); + std::string new_key; + if (!dev.upgradeKey(old_key, keyParams(rsa_key_size, rsa_exponent, ratelimit), &new_key)) + return -1; + if (!write_string_to_buf(new_key, key_buffer, key_buffer_size, key_out_size)) return -1; + return 0; +} + +KeymasterSignResult keymaster_sign_object_for_cryptfs_scrypt( + const uint8_t* key_blob, size_t key_blob_size, uint32_t ratelimit, const uint8_t* object, + const size_t object_size, uint8_t** signature_buffer, size_t* signature_buffer_size) { + Keymaster dev; + if (!dev) { + LOG(ERROR) << "Failed to initiate keymaster session" << std::endl; + return KeymasterSignResult::error; + } + if (!key_blob || !object || !signature_buffer || !signature_buffer_size) { + LOG(ERROR) << __FILE__ << ":" << __LINE__ << ":Invalid argument" << std::endl; + return KeymasterSignResult::error; + } + + km::AuthorizationSet outParams; + std::string key(reinterpret_cast(key_blob), key_blob_size); + std::string input(reinterpret_cast(object), object_size); + std::string output; + KeymasterOperation op; + + auto paramBuilder = km::AuthorizationSetBuilder().NoDigestOrPadding(); + while (true) { + op = dev.begin(km::KeyPurpose::SIGN, key, paramBuilder, km::HardwareAuthToken(), &outParams); + if (op.errorCode() == km::ErrorCode::KEY_RATE_LIMIT_EXCEEDED) { + sleep(ratelimit); + continue; + } else + break; + } + + if (op.errorCode() == km::ErrorCode::KEY_REQUIRES_UPGRADE) { + LOG(ERROR) << "Keymaster key requires upgrade" << std::endl; + return KeymasterSignResult::upgrade; + } + + if (op.errorCode() != km::ErrorCode::OK) { + LOG(ERROR) << "Error starting keymaster signature transaction: " << int32_t(op.errorCode()) << std::endl; + return KeymasterSignResult::error; + } + + if (!op.updateCompletely(input, &output)) { + LOG(ERROR) << "Error sending data to keymaster signature transaction: " + << uint32_t(op.errorCode()) << std::endl; + return KeymasterSignResult::error; + } + + if (!op.finish(&output)) { + LOG(ERROR) << "Error finalizing keymaster signature transaction: " + << int32_t(op.errorCode()) << std::endl; + return KeymasterSignResult::error; + } + + *signature_buffer = reinterpret_cast(malloc(output.size())); + if (*signature_buffer == nullptr) { + LOG(ERROR) << "Error allocation buffer for keymaster signature" << std::endl; + return KeymasterSignResult::error; + } + *signature_buffer_size = output.size(); + std::copy(output.data(), output.data() + output.size(), *signature_buffer); + return KeymasterSignResult::ok; +} diff --git a/crypto/ext4crypt/Keymaster4.h b/crypto/ext4crypt/Keymaster4.h new file mode 100644 index 000000000..29c73c682 --- /dev/null +++ b/crypto/ext4crypt/Keymaster4.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_TWRP_KEYMASTER_H +#define ANDROID_TWRP_KEYMASTER_H + +#include "KeyBuffer.h" + +#include +#include +#include + +#include +#include +#include + +namespace android { +namespace vold { + +namespace km = ::android::hardware::keymaster::V4_0; +using KmDevice = km::support::Keymaster; + +// C++ wrappers to the Keymaster hidl interface. +// This is tailored to the needs of KeyStorage, but could be extended to be +// a more general interface. + +// Wrapper for a Keymaster operation handle representing an +// ongoing Keymaster operation. Aborts the operation +// in the destructor if it is unfinished. Methods log failures +// to LOG(ERROR). +class KeymasterOperation { + public: + ~KeymasterOperation(); + // Is this instance valid? This is false if creation fails, and becomes + // false on finish or if an update fails. + explicit operator bool() { return mError == km::ErrorCode::OK; } + km::ErrorCode errorCode() { return mError; } + // Call "update" repeatedly until all of the input is consumed, and + // concatenate the output. Return true on success. + template + 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 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 mDevice; + DISALLOW_COPY_AND_ASSIGN(Keymaster); + static bool hmacKeyGenerated; +}; + +} // namespace vold +} // namespace android + +// FIXME no longer needed now cryptfs is in C++. + +/* + * The following functions provide C bindings to keymaster services + * needed by cryptfs scrypt. The compatibility check checks whether + * the keymaster implementation is considered secure, i.e., TEE backed. + * The create_key function generates an RSA key for signing. + * The sign_object function signes an object with the given keymaster + * key. + */ + +/* Return values for keymaster_sign_object_for_cryptfs_scrypt */ + +enum class KeymasterSignResult { + ok = 0, + error = -1, + upgrade = -2, +}; + +int keymaster_compatibility_cryptfs_scrypt(); +int keymaster_create_key_for_cryptfs_scrypt(uint32_t rsa_key_size, uint64_t rsa_exponent, + uint32_t ratelimit, uint8_t* key_buffer, + uint32_t key_buffer_size, uint32_t* key_out_size); + +int keymaster_upgrade_key_for_cryptfs_scrypt(uint32_t rsa_key_size, uint64_t rsa_exponent, + uint32_t ratelimit, const uint8_t* key_blob, + size_t key_blob_size, uint8_t* key_buffer, + uint32_t key_buffer_size, uint32_t* key_out_size); + +KeymasterSignResult keymaster_sign_object_for_cryptfs_scrypt( + const uint8_t* key_blob, size_t key_blob_size, uint32_t ratelimit, const uint8_t* object, + const size_t object_size, uint8_t** signature_buffer, size_t* signature_buffer_size); + +#endif diff --git a/crypto/ext4crypt/Utils.cpp b/crypto/ext4crypt/Utils.cpp index f0bf029b9..9711cef27 100644 --- a/crypto/ext4crypt/Utils.cpp +++ b/crypto/ext4crypt/Utils.cpp @@ -56,13 +56,13 @@ status_t ForkExecvp(const std::vector& args, security_context_t con } if (setexeccon(context)) { - LOG(ERROR) << "Failed to setexeccon"; + LOG(ERROR) << "Failed to setexeccon" << std::endl; abort(); } abort(); status_t res = 1;//android_fork_execvp(argc, argv, NULL, false, true); if (setexeccon(nullptr)) { - LOG(ERROR) << "Failed to setexeccon"; + LOG(ERROR) << "Failed to setexeccon" << std::endl; abort(); } @@ -89,17 +89,17 @@ status_t ForkExecvp(const std::vector& args, output.clear(); if (setexeccon(context)) { - LOG(ERROR) << "Failed to setexeccon"; + LOG(ERROR) << "Failed to setexeccon" << std::endl; abort(); } FILE* fp = popen(cmd.c_str(), "r"); if (setexeccon(nullptr)) { - LOG(ERROR) << "Failed to setexeccon"; + LOG(ERROR) << "Failed to setexeccon" << std::endl; abort(); } if (!fp) { - PLOG(ERROR) << "Failed to popen " << cmd; + PLOG(ERROR) << "Failed to popen " << cmd << std::endl; return -errno; } char line[1024]; @@ -108,7 +108,7 @@ status_t ForkExecvp(const std::vector& args, output.push_back(std::string(line)); } if (pclose(fp) != 0) { - PLOG(ERROR) << "Failed to pclose " << cmd; + PLOG(ERROR) << "Failed to pclose " << cmd << std::endl; return -errno; } @@ -134,14 +134,14 @@ pid_t ForkExecvpAsync(const std::vector& args) { close(STDERR_FILENO); if (execvp(argv[0], argv)) { - PLOG(ERROR) << "Failed to exec"; + PLOG(ERROR) << "Failed to exec" << std::endl; } _exit(1); } if (pid == -1) { - PLOG(ERROR) << "Failed to exec"; + PLOG(ERROR) << "Failed to exec" << std::endl; } free(argv); @@ -149,18 +149,20 @@ pid_t ForkExecvpAsync(const std::vector& args) { } status_t ReadRandomBytes(size_t bytes, std::string& out) { - out.clear(); + 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; } - char buf[BUFSIZ]; size_t n; - while ((n = TEMP_FAILURE_RETRY(read(fd, &buf[0], std::min(sizeof(buf), bytes)))) > 0) { - out.append(buf, n); + while ((n = TEMP_FAILURE_RETRY(read(fd, &buf[0], bytes))) > 0) { bytes -= n; + buf += n; } close(fd); @@ -247,6 +249,14 @@ std::string BuildDataMiscDePath(userid_t userId) { return StringPrintf("%s/misc_de/%u", BuildDataPath(nullptr).c_str(), userId); } +std::string BuildDataVendorCePath(userid_t userId) { + return StringPrintf("%s/vendor_ce/%u", BuildDataPath(nullptr).c_str(), userId); +} + +std::string BuildDataVendorDePath(userid_t userId) { + return StringPrintf("%s/vendor_de/%u", BuildDataPath(nullptr).c_str(), userId); +} + // Keep in sync with installd (frameworks/native/cmds/installd/utils.h) std::string BuildDataProfilesDePath(userid_t userId) { return StringPrintf("%s/misc/profiles/cur/%u", BuildDataPath(nullptr).c_str(), userId); diff --git a/crypto/ext4crypt/Utils.h b/crypto/ext4crypt/Utils.h index aede20341..253541cf5 100644 --- a/crypto/ext4crypt/Utils.h +++ b/crypto/ext4crypt/Utils.h @@ -47,6 +47,7 @@ status_t ForkExecvp(const std::vector& args, pid_t ForkExecvpAsync(const std::vector& args); status_t ReadRandomBytes(size_t bytes, std::string& out); +status_t ReadRandomBytes(size_t bytes, char* buffer); /* Converts hex string to raw bytes, ignoring [ :-] */ status_t HexToStr(const std::string& hex, std::string& str); @@ -61,6 +62,8 @@ std::string BuildDataMiscCePath(userid_t userid); std::string BuildDataMiscDePath(userid_t userid); std::string BuildDataProfilesDePath(userid_t userid); std::string BuildDataProfilesForeignDexDePath(userid_t userid); +std::string BuildDataVendorCePath(userid_t userid); +std::string BuildDataVendorDePath(userid_t userid); std::string BuildDataPath(const char* volumeUuid); std::string BuildDataMediaCePath(const char* volumeUuid, userid_t userid); diff --git a/crypto/ext4crypt/Weaver1.cpp b/crypto/ext4crypt/Weaver1.cpp index 6d09ec995..ea357edcc 100644 --- a/crypto/ext4crypt/Weaver1.cpp +++ b/crypto/ext4crypt/Weaver1.cpp @@ -24,9 +24,9 @@ #include "Weaver1.h" //#include -#include -#include -#include +//#include +//#include +//#include #include diff --git a/crypto/ext4crypt/keystore_auth.cpp b/crypto/ext4crypt/keystore_auth.cpp index 7d6eb24bf..40d890fa4 100644 --- a/crypto/ext4crypt/keystore_auth.cpp +++ b/crypto/ext4crypt/keystore_auth.cpp @@ -26,14 +26,20 @@ #include #include +#ifdef USE_SECURITY_NAMESPACE +#include +#else #include +#include +#endif #include #include #include -#include +#ifndef LOG_TAG #define LOG_TAG "keystore_auth" +#endif using namespace android; @@ -49,7 +55,7 @@ void create_error_file() { unlink("/auth_token"); } -int main(int argc, char *argv[]) { +int main() { unlink("/auth_error"); FILE* auth_file = fopen("/auth_token", "rb"); if (auth_file == NULL) { @@ -68,15 +74,26 @@ int main(int argc, char *argv[]) { // First get the keystore service sp sm = defaultServiceManager(); sp binder = sm->getService(String16("android.security.keystore")); +#ifdef USE_SECURITY_NAMESPACE + sp service = interface_cast(binder); +#else sp service = interface_cast(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 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"); diff --git a/mtp/Android.mk b/mtp/Android.mk index 32518d3fa..43649f794 100644 --- a/mtp/Android.mk +++ b/mtp/Android.mk @@ -5,7 +5,7 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := libtwrpmtp LOCAL_MODULE_TAGS := optional -LOCAL_CFLAGS = -D_FILE_OFFSET_BITS=64 -DMTP_DEVICE -DMTP_HOST -fno-strict-aliasing +LOCAL_CFLAGS = -D_FILE_OFFSET_BITS=64 -DMTP_DEVICE -DMTP_HOST -fno-strict-aliasing -Wno-unused-variable -Wno-format -Wno-unused-parameter -Wno-unused-private-field LOCAL_C_INCLUDES += $(LOCAL_PATH) bionic frameworks/base/include system/core/include bionic/libc/private/ ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 23; echo $$?),0) LOCAL_C_INCLUDES += external/stlport/stlport diff --git a/partition.cpp b/partition.cpp index 0272708cd..dfa57fafc 100644 --- a/partition.cpp +++ b/partition.cpp @@ -130,6 +130,7 @@ enum TW_FSTAB_FLAGS { TWFLAG_CANENCRYPTBACKUP, TWFLAG_DISPLAY, TWFLAG_ENCRYPTABLE, + TWFLAG_FILEENCRYPTION, TWFLAG_FLASHIMG, TWFLAG_FORCEENCRYPT, TWFLAG_FSFLAGS, @@ -172,6 +173,7 @@ const struct flag_list tw_flags[] = { { "defaults", TWFLAG_DEFAULTS }, { "display=", TWFLAG_DISPLAY }, { "encryptable=", TWFLAG_ENCRYPTABLE }, + { "fileencryption=", TWFLAG_FILEENCRYPTION }, { "flashimg", TWFLAG_FLASHIMG }, { "forceencrypt=", TWFLAG_FORCEENCRYPT }, { "fsflags=", TWFLAG_FSFLAGS }, @@ -822,6 +824,24 @@ void TWPartition::Apply_TW_Flag(const unsigned flag, const char* str, const bool case TWFLAG_FORCEENCRYPT: Crypto_Key_Location = str; break; + case TWFLAG_FILEENCRYPTION: + // This flag isn't used by TWRP but is needed in 9.0 FBE decrypt + // fileencryption=ice:aes-256-heh + { + std::string FBE = str; + std::string FBE_contents, FBE_filenames; + size_t colon_loc = FBE.find(":"); + if (colon_loc == std::string::npos) { + LOGINFO("Invalid fileencryption fstab flag: '%s'\n", str); + break; + } + FBE_contents = FBE.substr(0, colon_loc); + FBE_filenames = FBE.substr(colon_loc + 1); + property_set("fbe.contents", FBE_contents.c_str()); + property_set("fbe.filenames", FBE_filenames.c_str()); + LOGINFO("FBE contents '%s', filenames '%s'\n", FBE_contents.c_str(), FBE_filenames.c_str()); + } + break; case TWFLAG_FLASHIMG: Can_Flash_Img = val; break; diff --git a/prebuilt/Android.mk b/prebuilt/Android.mk index fb27f0a64..8471ffba1 100644 --- a/prebuilt/Android.mk +++ b/prebuilt/Android.mk @@ -212,6 +212,17 @@ ifeq ($(TW_INCLUDE_CRYPTO), true) ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 28; echo $$?),0) RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libsoftkeymaster.so endif + ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 28; echo $$?),0) + RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/android.hardware.keymaster@4.0.so + RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libkeymaster4support.so + RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libkeystore_aidl.so + RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libkeystore_parcelables.so + RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libutilscallstack.so + RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libunwindstack.so + RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libdexfile.so + RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libservices.so + RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libkeymaster_portable.so + endif endif endif ifeq ($(AB_OTA_UPDATER), true) @@ -329,7 +340,7 @@ endif TWRP_AUTOGEN := $(intermediates)/teamwin GEN := $(intermediates)/teamwin -$(GEN): $(RELINK) $(TW_BB_SYMLINKS) +$(GEN): $(RELINK) $(TW_BB_SYMLINKS) toolbox_symlinks $(GEN): $(RELINK_SOURCE_FILES) $(call intermediates-dir-for,EXECUTABLES,init)/init $(RELINK) $(TARGET_RECOVERY_ROOT_OUT)/sbin $(RELINK_SOURCE_FILES) diff --git a/res-hdpi/images/erasing_text.png b/res-hdpi/images/erasing_text.png deleted file mode 100644 index 34c56a966..000000000 Binary files a/res-hdpi/images/erasing_text.png and /dev/null differ diff --git a/res-hdpi/images/error_text.png b/res-hdpi/images/error_text.png deleted file mode 100644 index 2a96053da..000000000 Binary files a/res-hdpi/images/error_text.png and /dev/null differ diff --git a/res-hdpi/images/installing_security_text.png b/res-hdpi/images/installing_security_text.png deleted file mode 100644 index 97e1f11b3..000000000 Binary files a/res-hdpi/images/installing_security_text.png and /dev/null differ diff --git a/res-hdpi/images/installing_text.png b/res-hdpi/images/installing_text.png deleted file mode 100644 index 1d591eb8b..000000000 Binary files a/res-hdpi/images/installing_text.png and /dev/null differ diff --git a/res-hdpi/images/no_command_text.png b/res-hdpi/images/no_command_text.png deleted file mode 100644 index 977fcfaff..000000000 Binary files a/res-hdpi/images/no_command_text.png and /dev/null differ diff --git a/res-mdpi/images/erasing_text.png b/res-mdpi/images/erasing_text.png deleted file mode 100644 index dcd0ea656..000000000 Binary files a/res-mdpi/images/erasing_text.png and /dev/null differ diff --git a/res-mdpi/images/error_text.png b/res-mdpi/images/error_text.png deleted file mode 100644 index 2152dad83..000000000 Binary files a/res-mdpi/images/error_text.png and /dev/null differ diff --git a/res-mdpi/images/installing_security_text.png b/res-mdpi/images/installing_security_text.png deleted file mode 100644 index d1ac4cad6..000000000 Binary files a/res-mdpi/images/installing_security_text.png and /dev/null differ diff --git a/res-mdpi/images/installing_text.png b/res-mdpi/images/installing_text.png deleted file mode 100644 index c9b6d7185..000000000 Binary files a/res-mdpi/images/installing_text.png and /dev/null differ diff --git a/res-mdpi/images/no_command_text.png b/res-mdpi/images/no_command_text.png deleted file mode 100644 index f77ad1325..000000000 Binary files a/res-mdpi/images/no_command_text.png and /dev/null differ diff --git a/res-xhdpi/images/erasing_text.png b/res-xhdpi/images/erasing_text.png deleted file mode 100644 index e22b27479..000000000 Binary files a/res-xhdpi/images/erasing_text.png and /dev/null differ diff --git a/res-xhdpi/images/error_text.png b/res-xhdpi/images/error_text.png deleted file mode 100644 index e4c27e1fc..000000000 Binary files a/res-xhdpi/images/error_text.png and /dev/null differ diff --git a/res-xhdpi/images/installing_security_text.png b/res-xhdpi/images/installing_security_text.png deleted file mode 100644 index 7ba12b667..000000000 Binary files a/res-xhdpi/images/installing_security_text.png and /dev/null differ diff --git a/res-xhdpi/images/installing_text.png b/res-xhdpi/images/installing_text.png deleted file mode 100644 index 567988e7f..000000000 Binary files a/res-xhdpi/images/installing_text.png and /dev/null differ diff --git a/res-xhdpi/images/no_command_text.png b/res-xhdpi/images/no_command_text.png deleted file mode 100644 index a682abbef..000000000 Binary files a/res-xhdpi/images/no_command_text.png and /dev/null differ diff --git a/res-xxhdpi/images/erasing_text.png b/res-xxhdpi/images/erasing_text.png deleted file mode 100644 index 6cc953b6d..000000000 Binary files a/res-xxhdpi/images/erasing_text.png and /dev/null differ diff --git a/res-xxhdpi/images/error_text.png b/res-xxhdpi/images/error_text.png deleted file mode 100644 index 0d5cea843..000000000 Binary files a/res-xxhdpi/images/error_text.png and /dev/null differ diff --git a/res-xxhdpi/images/installing_security_text.png b/res-xxhdpi/images/installing_security_text.png deleted file mode 100644 index 5d105986a..000000000 Binary files a/res-xxhdpi/images/installing_security_text.png and /dev/null differ diff --git a/res-xxhdpi/images/installing_text.png b/res-xxhdpi/images/installing_text.png deleted file mode 100644 index 6e94fa28b..000000000 Binary files a/res-xxhdpi/images/installing_text.png and /dev/null differ diff --git a/res-xxhdpi/images/no_command_text.png b/res-xxhdpi/images/no_command_text.png deleted file mode 100644 index 40ab484d9..000000000 Binary files a/res-xxhdpi/images/no_command_text.png and /dev/null differ diff --git a/res-xxxhdpi/images/erasing_text.png b/res-xxxhdpi/images/erasing_text.png deleted file mode 100644 index cc730992b..000000000 Binary files a/res-xxxhdpi/images/erasing_text.png and /dev/null differ diff --git a/res-xxxhdpi/images/error_text.png b/res-xxxhdpi/images/error_text.png deleted file mode 100644 index fea3cfc95..000000000 Binary files a/res-xxxhdpi/images/error_text.png and /dev/null differ diff --git a/res-xxxhdpi/images/installing_security_text.png b/res-xxxhdpi/images/installing_security_text.png deleted file mode 100644 index ed77d0889..000000000 Binary files a/res-xxxhdpi/images/installing_security_text.png and /dev/null differ diff --git a/res-xxxhdpi/images/installing_text.png b/res-xxxhdpi/images/installing_text.png deleted file mode 100644 index 965910648..000000000 Binary files a/res-xxxhdpi/images/installing_text.png and /dev/null differ diff --git a/res-xxxhdpi/images/no_command_text.png b/res-xxxhdpi/images/no_command_text.png deleted file mode 100644 index 4e6f3639d..000000000 Binary files a/res-xxxhdpi/images/no_command_text.png and /dev/null differ -- cgit v1.2.3