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
@@ -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)
|
||||
|
||||
@@ -15,7 +15,11 @@
|
||||
*/
|
||||
|
||||
#include "Decrypt.h"
|
||||
#ifdef USE_KEYSTORAGE_4
|
||||
#include "Ext4CryptPie.h"
|
||||
#else
|
||||
#include "Ext4Crypt.h"
|
||||
#endif
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
@@ -50,12 +54,16 @@
|
||||
|
||||
#include <ext4_utils/ext4_crypt.h>
|
||||
|
||||
#ifdef USE_KEYSTORAGE_4
|
||||
#include <android/security/IKeystoreService.h>
|
||||
#else
|
||||
#include <keystore/IKeystoreService.h>
|
||||
#include <keystore/authorization_set.h>
|
||||
#endif
|
||||
#include <binder/IPCThreadState.h>
|
||||
#include <binder/IServiceManager.h>
|
||||
|
||||
#include <keystore/keystore.h>
|
||||
#include <keystore/authorization_set.h>
|
||||
|
||||
#include <algorithm>
|
||||
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<IBinder> binder = getKeystoreBinderRetry();
|
||||
#ifdef USE_KEYSTORAGE_4
|
||||
sp<security::IKeystoreService> service = interface_cast<security::IKeystoreService>(binder);
|
||||
#else
|
||||
sp<IKeystoreService> service = interface_cast<IKeystoreService>(binder);
|
||||
#endif
|
||||
if (service == NULL) {
|
||||
printf("error: could not connect to keystore service\n");
|
||||
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<uint8_t> 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<uint8_t> 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<uint8_t> 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<uint8_t> 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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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 <algorithm>
|
||||
|
||||
861
crypto/ext4crypt/Ext4CryptPie.cpp
Normal file
@@ -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 <algorithm>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
#include <selinux/android.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <private/android_filesystem_config.h>
|
||||
|
||||
//#include "android/os/IVold.h"
|
||||
|
||||
//#include "cryptfs.h"
|
||||
|
||||
#define EMULATED_USES_SELINUX 0
|
||||
#define MANAGE_MISC_DIRS 0
|
||||
|
||||
#include <cutils/fs.h>
|
||||
#include <cutils/properties.h>
|
||||
|
||||
#include <ext4_utils/ext4_crypt.h>
|
||||
#include <keyutils.h>
|
||||
|
||||
#include <android-base/file.h>
|
||||
//#include <android-base/logging.h>
|
||||
#include <android-base/properties.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
|
||||
#include <iostream>
|
||||
#define LOG(x) std::cout
|
||||
#define PLOG(x) std::cout
|
||||
#define DATA_MNT_POINT "/data"
|
||||
|
||||
using android::base::StringPrintf;
|
||||
using android::base::WriteStringToFile;
|
||||
using android::vold::kEmptyAuthentication;
|
||||
using android::vold::KeyBuffer;
|
||||
|
||||
// Store main DE raw ref / policy
|
||||
std::string de_raw_ref;
|
||||
// Map user ids to key references
|
||||
std::map<userid_t, std::string> s_de_key_raw_refs;
|
||||
std::map<userid_t, std::string> s_ce_key_raw_refs;
|
||||
// TODO abolish this map, per b/26948053
|
||||
std::map<userid_t, KeyBuffer> s_ce_keys;
|
||||
|
||||
namespace {
|
||||
|
||||
struct PolicyKeyRef {
|
||||
std::string contents_mode;
|
||||
std::string filenames_mode;
|
||||
std::string key_raw_ref;
|
||||
};
|
||||
|
||||
const std::string device_key_dir = std::string() + DATA_MNT_POINT + e4crypt_unencrypted_folder;
|
||||
const std::string device_key_path = device_key_dir + "/key";
|
||||
const std::string device_key_temp = device_key_dir + "/temp";
|
||||
|
||||
const std::string user_key_dir = std::string() + DATA_MNT_POINT + "/misc/vold/user_keys";
|
||||
const std::string user_key_temp = user_key_dir + "/temp";
|
||||
const std::string prepare_subdirs_path = "/system/bin/vold_prepare_subdirs";
|
||||
|
||||
const std::string systemwide_volume_key_dir =
|
||||
std::string() + DATA_MNT_POINT + "/misc/vold/volume_keys";
|
||||
|
||||
bool s_global_de_initialized = false;
|
||||
|
||||
// Some users are ephemeral, don't try to wipe their keys from disk
|
||||
std::set<userid_t> s_ephemeral_users;
|
||||
|
||||
}
|
||||
|
||||
static bool e4crypt_is_emulated() {
|
||||
return property_get_bool("persist.sys.emulate_fbe", false);
|
||||
}
|
||||
|
||||
static const char* escape_empty(const std::string& value) {
|
||||
return value.empty() ? "null" : value.c_str();
|
||||
}
|
||||
|
||||
static std::string get_de_key_path(userid_t user_id) {
|
||||
return StringPrintf("%s/de/%d", user_key_dir.c_str(), user_id);
|
||||
}
|
||||
|
||||
static std::string get_ce_key_directory_path(userid_t user_id) {
|
||||
return StringPrintf("%s/ce/%d", user_key_dir.c_str(), user_id);
|
||||
}
|
||||
|
||||
// Returns the keys newest first
|
||||
static std::vector<std::string> get_ce_key_paths(const std::string& directory_path) {
|
||||
auto dirp = std::unique_ptr<DIR, int (*)(DIR*)>(opendir(directory_path.c_str()), closedir);
|
||||
if (!dirp) {
|
||||
PLOG(ERROR) << "Unable to open ce key directory: " + directory_path << std::endl;
|
||||
return std::vector<std::string>();
|
||||
}
|
||||
std::vector<std::string> result;
|
||||
for (;;) {
|
||||
errno = 0;
|
||||
auto const entry = readdir(dirp.get());
|
||||
if (!entry) {
|
||||
if (errno) {
|
||||
PLOG(ERROR) << "Unable to read ce key directory: " + directory_path << std::endl;
|
||||
return std::vector<std::string>();
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (entry->d_type != DT_DIR || entry->d_name[0] != 'c') {
|
||||
LOG(DEBUG) << "Skipping non-key " << entry->d_name << std::endl;
|
||||
continue;
|
||||
}
|
||||
result.emplace_back(directory_path + "/" + entry->d_name);
|
||||
}
|
||||
std::sort(result.begin(), result.end());
|
||||
std::reverse(result.begin(), result.end());
|
||||
return result;
|
||||
}
|
||||
|
||||
static std::string get_ce_key_current_path(const std::string& directory_path) {
|
||||
return directory_path + "/current";
|
||||
}
|
||||
|
||||
/*static bool get_ce_key_new_path(const std::string& directory_path,
|
||||
const std::vector<std::string>& paths,
|
||||
std::string *ce_key_path) {
|
||||
if (paths.empty()) {
|
||||
*ce_key_path = get_ce_key_current_path(directory_path);
|
||||
return true;
|
||||
}
|
||||
for (unsigned int i = 0; i < UINT_MAX; i++) {
|
||||
auto const candidate = StringPrintf("%s/cx%010u", directory_path.c_str(), i);
|
||||
if (paths[0] < candidate) {
|
||||
*ce_key_path = candidate;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}*/
|
||||
|
||||
// Discard all keys but the named one; rename it to canonical name.
|
||||
// No point in acting on errors in this; ignore them.
|
||||
static void fixate_user_ce_key(const std::string& directory_path, const std::string &to_fix,
|
||||
const std::vector<std::string>& paths) {
|
||||
for (auto const other_path: paths) {
|
||||
if (other_path != to_fix) {
|
||||
android::vold::destroyKey(other_path);
|
||||
}
|
||||
}
|
||||
auto const current_path = get_ce_key_current_path(directory_path);
|
||||
if (to_fix != current_path) {
|
||||
LOG(DEBUG) << "Renaming " << to_fix << " to " << current_path << 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<userid_t, std::string>& key_map, userid_t user_id,
|
||||
std::string* raw_ref) {
|
||||
auto refi = key_map.find(user_id);
|
||||
if (refi == key_map.end()) {
|
||||
LOG(ERROR) << "Cannot find key for " << user_id << std::endl;
|
||||
return false;
|
||||
}
|
||||
*raw_ref = refi->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void get_data_file_encryption_modes(PolicyKeyRef* key_ref) {
|
||||
/*struct fstab_rec* rec = fs_mgr_get_entry_for_mount_point(fstab_default, DATA_MNT_POINT);
|
||||
char const* contents_mode = strdup("ice");
|
||||
char const* filenames_mode = strdup("aes-256-heh");
|
||||
fs_mgr_get_file_encryption_modes(rec, &contents_mode, &filenames_mode);
|
||||
key_ref->contents_mode = contents_mode;
|
||||
key_ref->filenames_mode = filenames_mode;*/
|
||||
LOG(INFO) << "contents mode '" << android::base::GetProperty("fbe.contents", "aes-256-xts") << "' filenames '" << android::base::GetProperty("fbe.filenames", "aes-256-heh") << "'\n";
|
||||
key_ref->contents_mode =
|
||||
android::base::GetProperty("fbe.contents", "aes-256-xts");
|
||||
key_ref->filenames_mode =
|
||||
android::base::GetProperty("fbe.filenames", "aes-256-heh");
|
||||
}
|
||||
|
||||
static bool 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<DIR, int (*)(DIR*)>(opendir(de_dir.c_str()), closedir);
|
||||
if (!dirp) {
|
||||
PLOG(ERROR) << "Unable to read de key directory" << std::endl;
|
||||
return false;
|
||||
}
|
||||
for (;;) {
|
||||
errno = 0;
|
||||
auto entry = readdir(dirp.get());
|
||||
if (!entry) {
|
||||
if (errno) {
|
||||
PLOG(ERROR) << "Unable to read de key directory" << std::endl;
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (entry->d_type != DT_DIR || !is_numeric(entry->d_name)) {
|
||||
LOG(DEBUG) << "Skipping non-de-key " << entry->d_name << std::endl;
|
||||
continue;
|
||||
}
|
||||
userid_t user_id = std::stoi(entry->d_name);
|
||||
if (s_de_key_raw_refs.count(user_id) == 0) {
|
||||
auto key_path = de_dir + "/" + entry->d_name;
|
||||
KeyBuffer key;
|
||||
if (!android::vold::retrieveKey(key_path, kEmptyAuthentication, &key)) return false;
|
||||
std::string raw_ref;
|
||||
if (!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<std::string>{prepare_subdirs_path, action, volume_uuid,
|
||||
std::to_string(user_id), std::to_string(flags)})) {
|
||||
LOG(ERROR) << "vold_prepare_subdirs failed";
|
||||
return false;
|
||||
}
|
||||
return true;*/
|
||||
}
|
||||
|
||||
bool 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<DIR, int (*)(DIR*)>(opendir(directory_path.c_str()), closedir);
|
||||
if (!dirp) {
|
||||
PLOG(ERROR) << "Unable to open directory: " + directory_path;
|
||||
return false;
|
||||
}
|
||||
bool res = true;
|
||||
for (;;) {
|
||||
errno = 0;
|
||||
auto const entry = readdir(dirp.get());
|
||||
if (!entry) {
|
||||
if (errno) {
|
||||
PLOG(ERROR) << "Unable to read directory: " + directory_path;
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (entry->d_type != DT_DIR || entry->d_name[0] == '.') {
|
||||
LOG(DEBUG) << "Skipping non-user " << entry->d_name;
|
||||
continue;
|
||||
}
|
||||
res &= destroy_volkey(directory_path + "/" + entry->d_name, volume_uuid);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
bool 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;
|
||||
}*/
|
||||
42
crypto/ext4crypt/Ext4CryptPie.h
Normal file
@@ -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 <map>
|
||||
#include <string>
|
||||
|
||||
#include <cutils/multiuser.h>
|
||||
|
||||
bool e4crypt_initialize_global_de();
|
||||
|
||||
bool e4crypt_init_user0();
|
||||
/*bool e4crypt_vold_create_user_key(userid_t user_id, int serial, bool ephemeral);
|
||||
bool e4crypt_destroy_user_key(userid_t user_id);
|
||||
bool e4crypt_add_user_key_auth(userid_t user_id, int serial, const std::string& token,
|
||||
const std::string& secret);
|
||||
bool e4crypt_fixate_newest_user_key_auth(userid_t user_id);*/
|
||||
|
||||
bool e4crypt_unlock_user_key(userid_t user_id, int serial, const std::string& token,
|
||||
const std::string& secret);
|
||||
//bool e4crypt_lock_user_key(userid_t user_id);
|
||||
|
||||
bool e4crypt_prepare_user_storage(const std::string& volume_uuid, userid_t user_id, int serial,
|
||||
int flags);
|
||||
/*bool e4crypt_destroy_user_storage(const std::string& volume_uuid, userid_t user_id, int flags);
|
||||
|
||||
bool e4crypt_destroy_volume_keys(const std::string& volume_uuid);*/
|
||||
|
||||
bool lookup_key_ref(const std::map<userid_t, std::string>& key_map, userid_t user_id,
|
||||
std::string* raw_ref);
|
||||
37
crypto/ext4crypt/KeyBuffer.cpp
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "KeyBuffer.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
namespace android {
|
||||
namespace vold {
|
||||
|
||||
KeyBuffer operator+(KeyBuffer&& lhs, const KeyBuffer& rhs) {
|
||||
std::copy(rhs.begin(), rhs.end(), std::back_inserter(lhs));
|
||||
return std::move(lhs);
|
||||
}
|
||||
|
||||
KeyBuffer operator+(KeyBuffer&& lhs, const char* rhs) {
|
||||
std::copy(rhs, rhs + strlen(rhs), std::back_inserter(lhs));
|
||||
return std::move(lhs);
|
||||
}
|
||||
|
||||
} // namespace vold
|
||||
} // namespace android
|
||||
|
||||
63
crypto/ext4crypt/KeyBuffer.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef ANDROID_VOLD_KEYBUFFER_H
|
||||
#define ANDROID_VOLD_KEYBUFFER_H
|
||||
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace android {
|
||||
namespace vold {
|
||||
|
||||
/**
|
||||
* Variant of memset() that should never be optimized away. Borrowed from keymaster code.
|
||||
*/
|
||||
#ifdef __clang__
|
||||
#define OPTNONE __attribute__((optnone))
|
||||
#else // not __clang__
|
||||
#define OPTNONE __attribute__((optimize("O0")))
|
||||
#endif // not __clang__
|
||||
inline OPTNONE void* memset_s(void* s, int c, size_t n) {
|
||||
if (!s)
|
||||
return s;
|
||||
return memset(s, c, n);
|
||||
}
|
||||
#undef OPTNONE
|
||||
|
||||
// Allocator that delegates useful work to standard one but zeroes data before deallocating.
|
||||
class ZeroingAllocator : public std::allocator<char> {
|
||||
public:
|
||||
void deallocate(pointer p, size_type n)
|
||||
{
|
||||
memset_s(p, 0, n);
|
||||
std::allocator<char>::deallocate(p, n);
|
||||
}
|
||||
};
|
||||
|
||||
// Char vector that zeroes memory when deallocating.
|
||||
using KeyBuffer = std::vector<char, ZeroingAllocator>;
|
||||
|
||||
// Convenience methods to concatenate key buffers.
|
||||
KeyBuffer operator+(KeyBuffer&& lhs, const KeyBuffer& rhs);
|
||||
KeyBuffer operator+(KeyBuffer&& lhs, const char* rhs);
|
||||
|
||||
} // namespace vold
|
||||
} // namespace android
|
||||
|
||||
#endif
|
||||
|
||||
598
crypto/ext4crypt/KeyStorage4.cpp
Normal file
@@ -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 <vector>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/sha.h>
|
||||
|
||||
#include <android-base/file.h>
|
||||
//#include <android-base/logging.h>
|
||||
#include <android-base/unique_fd.h>
|
||||
|
||||
#include <cutils/properties.h>
|
||||
|
||||
#include <hardware/hw_auth_token.h>
|
||||
#include <keymasterV4_0/authorization_set.h>
|
||||
#include <keymasterV4_0/keymaster_utils.h>
|
||||
|
||||
#include <iostream>
|
||||
#define ERROR 1
|
||||
#define LOG(x) std::cout
|
||||
#define PLOG(x) std::cout
|
||||
|
||||
extern "C" {
|
||||
|
||||
#include "crypto_scrypt.h"
|
||||
}
|
||||
|
||||
namespace android {
|
||||
namespace vold {
|
||||
|
||||
const KeyAuthentication kEmptyAuthentication{"", ""};
|
||||
|
||||
static constexpr size_t AES_KEY_BYTES = 32;
|
||||
static constexpr size_t GCM_NONCE_BYTES = 12;
|
||||
static constexpr size_t GCM_MAC_BYTES = 16;
|
||||
static constexpr size_t SALT_BYTES = 1 << 4;
|
||||
static constexpr size_t SECDISCARDABLE_BYTES = 1 << 14;
|
||||
static constexpr size_t STRETCHED_BYTES = 1 << 6;
|
||||
|
||||
static constexpr uint32_t AUTH_TIMEOUT = 30; // Seconds
|
||||
|
||||
static const char* kCurrentVersion = "1";
|
||||
static const char* kRmPath = "/system/bin/rm";
|
||||
static const char* kSecdiscardPath = "/system/bin/secdiscard";
|
||||
static const char* kStretch_none = "none";
|
||||
static const char* kStretch_nopassword = "nopassword";
|
||||
static const std::string kStretchPrefix_scrypt = "scrypt ";
|
||||
static const char* kHashPrefix_secdiscardable = "Android secdiscardable SHA512";
|
||||
static const char* kHashPrefix_keygen = "Android key wrapping key generation SHA512";
|
||||
static const char* kFn_encrypted_key = "encrypted_key";
|
||||
static const char* kFn_keymaster_key_blob = "keymaster_key_blob";
|
||||
static const char* kFn_keymaster_key_blob_upgraded = "keymaster_key_blob_upgraded";
|
||||
static const char* kFn_salt = "salt";
|
||||
static const char* kFn_secdiscardable = "secdiscardable";
|
||||
static const char* kFn_stretching = "stretching";
|
||||
static const char* kFn_version = "version";
|
||||
|
||||
static bool checkSize(const std::string& kind, size_t actual, size_t expected) {
|
||||
if (actual != expected) {
|
||||
LOG(ERROR) << "Wrong number of bytes in " << kind << ", expected " << expected << " got "
|
||||
<< actual << std::endl;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void hashWithPrefix(char const* prefix, const std::string& tohash, std::string* res) {
|
||||
SHA512_CTX c;
|
||||
|
||||
SHA512_Init(&c);
|
||||
// Personalise the hashing by introducing a fixed prefix.
|
||||
// Hashing applications should use personalization except when there is a
|
||||
// specific reason not to; see section 4.11 of https://www.schneier.com/skein1.3.pdf
|
||||
std::string hashingPrefix = prefix;
|
||||
hashingPrefix.resize(SHA512_CBLOCK);
|
||||
SHA512_Update(&c, hashingPrefix.data(), hashingPrefix.size());
|
||||
SHA512_Update(&c, tohash.data(), tohash.size());
|
||||
res->assign(SHA512_DIGEST_LENGTH, '\0');
|
||||
SHA512_Final(reinterpret_cast<uint8_t*>(&(*res)[0]), &c);
|
||||
}
|
||||
|
||||
static bool generateKeymasterKey(Keymaster& keymaster, const KeyAuthentication& auth,
|
||||
const std::string& appId, std::string* key) {
|
||||
auto paramBuilder = km::AuthorizationSetBuilder()
|
||||
.AesEncryptionKey(AES_KEY_BYTES * 8)
|
||||
.GcmModeMinMacLen(GCM_MAC_BYTES * 8)
|
||||
.Authorization(km::TAG_APPLICATION_ID, km::support::blob2hidlVec(appId));
|
||||
if (auth.token.empty()) {
|
||||
LOG(DEBUG) << "Creating key that doesn't need auth token" << std::endl;
|
||||
paramBuilder.Authorization(km::TAG_NO_AUTH_REQUIRED);
|
||||
} else {
|
||||
LOG(DEBUG) << "Auth token required for key" << std::endl;
|
||||
if (auth.token.size() != sizeof(hw_auth_token_t)) {
|
||||
LOG(ERROR) << "Auth token should be " << sizeof(hw_auth_token_t) << " bytes, was "
|
||||
<< auth.token.size() << " bytes" << std::endl;
|
||||
return false;
|
||||
}
|
||||
const hw_auth_token_t* at = reinterpret_cast<const hw_auth_token_t*>(auth.token.data());
|
||||
paramBuilder.Authorization(km::TAG_USER_SECURE_ID, at->user_id);
|
||||
paramBuilder.Authorization(km::TAG_USER_AUTH_TYPE, km::HardwareAuthenticatorType::PASSWORD);
|
||||
paramBuilder.Authorization(km::TAG_AUTH_TIMEOUT, AUTH_TIMEOUT);
|
||||
}
|
||||
return keymaster.generateKey(paramBuilder, key);
|
||||
}
|
||||
|
||||
static std::pair<km::AuthorizationSet, km::HardwareAuthToken> beginParams(
|
||||
const KeyAuthentication& auth, const std::string& appId) {
|
||||
auto paramBuilder = km::AuthorizationSetBuilder()
|
||||
.GcmModeMacLen(GCM_MAC_BYTES * 8)
|
||||
.Authorization(km::TAG_APPLICATION_ID, km::support::blob2hidlVec(appId));
|
||||
km::HardwareAuthToken authToken;
|
||||
if (!auth.token.empty()) {
|
||||
LOG(DEBUG) << "Supplying auth token to Keymaster" << std::endl;
|
||||
authToken = km::support::hidlVec2AuthToken(km::support::blob2hidlVec(auth.token));
|
||||
}
|
||||
return {paramBuilder, authToken};
|
||||
}
|
||||
|
||||
static bool readFileToString(const std::string& filename, std::string* result) {
|
||||
if (!android::base::ReadFileToString(filename, result)) {
|
||||
PLOG(ERROR) << "Failed to read from " << filename << std::endl;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool writeStringToFile(const std::string& payload, const std::string& filename) {
|
||||
PLOG(ERROR) << __FUNCTION__ << " called for " << filename << " and being skipped\n";
|
||||
return true;
|
||||
android::base::unique_fd fd(TEMP_FAILURE_RETRY(
|
||||
open(filename.c_str(), O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC | O_CLOEXEC, 0666)));
|
||||
if (fd == -1) {
|
||||
PLOG(ERROR) << "Failed to open " << filename;
|
||||
return false;
|
||||
}
|
||||
if (!android::base::WriteStringToFd(payload, fd)) {
|
||||
PLOG(ERROR) << "Failed to write to " << filename;
|
||||
unlink(filename.c_str());
|
||||
return false;
|
||||
}
|
||||
// fsync as close won't guarantee flush data
|
||||
// see close(2), fsync(2) and b/68901441
|
||||
if (fsync(fd) == -1) {
|
||||
if (errno == EROFS || errno == EINVAL) {
|
||||
PLOG(WARNING) << "Skip fsync " << filename
|
||||
<< " on a file system does not support synchronization";
|
||||
} else {
|
||||
PLOG(ERROR) << "Failed to fsync " << filename;
|
||||
unlink(filename.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool readRandomBytesOrLog(size_t count, std::string* out) {
|
||||
auto status = ReadRandomBytes(count, *out);
|
||||
if (status != OK) {
|
||||
LOG(ERROR) << "Random read failed with status: " << status << std::endl;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool createSecdiscardable(const std::string& filename, std::string* hash) {
|
||||
std::string secdiscardable;
|
||||
if (!readRandomBytesOrLog(SECDISCARDABLE_BYTES, &secdiscardable)) return false;
|
||||
if (!writeStringToFile(secdiscardable, filename)) return false;
|
||||
hashWithPrefix(kHashPrefix_secdiscardable, secdiscardable, hash);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool readSecdiscardable(const std::string& filename, std::string* hash) {
|
||||
std::string secdiscardable;
|
||||
if (!readFileToString(filename, &secdiscardable)) return false;
|
||||
hashWithPrefix(kHashPrefix_secdiscardable, secdiscardable, hash);
|
||||
return true;
|
||||
}
|
||||
|
||||
static KeymasterOperation begin(Keymaster& keymaster, const std::string& dir,
|
||||
km::KeyPurpose purpose, const km::AuthorizationSet& keyParams,
|
||||
const km::AuthorizationSet& opParams,
|
||||
const km::HardwareAuthToken& authToken,
|
||||
km::AuthorizationSet* outParams) {
|
||||
auto kmKeyPath = dir + "/" + kFn_keymaster_key_blob;
|
||||
std::string kmKey;
|
||||
if (!readFileToString(kmKeyPath, &kmKey)) return KeymasterOperation();
|
||||
km::AuthorizationSet inParams(keyParams);
|
||||
inParams.append(opParams.begin(), opParams.end());
|
||||
for (;;) {
|
||||
auto opHandle = keymaster.begin(purpose, kmKey, inParams, authToken, outParams);
|
||||
if (opHandle) {
|
||||
return opHandle;
|
||||
}
|
||||
if (opHandle.errorCode() != km::ErrorCode::KEY_REQUIRES_UPGRADE) return opHandle;
|
||||
LOG(DEBUG) << "Upgrading key in memory only: " << dir << std::endl;
|
||||
std::string newKey;
|
||||
if (!keymaster.upgradeKey(kmKey, keyParams, &newKey)) return KeymasterOperation();
|
||||
/*auto newKeyPath = dir + "/" + kFn_keymaster_key_blob_upgraded;
|
||||
if (!writeStringToFile(newKey, newKeyPath)) return KeymasterOperation();
|
||||
if (rename(newKeyPath.c_str(), kmKeyPath.c_str()) != 0) {
|
||||
PLOG(ERROR) << "Unable to move upgraded key to location: " << kmKeyPath;
|
||||
return KeymasterOperation();
|
||||
}
|
||||
if (!keymaster.deleteKey(kmKey)) {
|
||||
LOG(ERROR) << "Key deletion failed during upgrade, continuing anyway: " << dir;
|
||||
}*/
|
||||
kmKey = newKey;
|
||||
LOG(INFO) << "Key upgraded in memory but not updated in folder: " << dir << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
static bool encryptWithKeymasterKey(Keymaster& keymaster, const std::string& dir,
|
||||
const km::AuthorizationSet& keyParams,
|
||||
const km::HardwareAuthToken& authToken,
|
||||
const KeyBuffer& message, std::string* ciphertext) {
|
||||
km::AuthorizationSet opParams;
|
||||
km::AuthorizationSet outParams;
|
||||
auto opHandle =
|
||||
begin(keymaster, dir, km::KeyPurpose::ENCRYPT, keyParams, opParams, authToken, &outParams);
|
||||
if (!opHandle) return false;
|
||||
auto nonceBlob = outParams.GetTagValue(km::TAG_NONCE);
|
||||
if (!nonceBlob.isOk()) {
|
||||
LOG(ERROR) << "GCM encryption but no nonce generated" << std::endl;
|
||||
return false;
|
||||
}
|
||||
// nonceBlob here is just a pointer into existing data, must not be freed
|
||||
std::string nonce(reinterpret_cast<const char*>(&nonceBlob.value()[0]),
|
||||
nonceBlob.value().size());
|
||||
if (!checkSize("nonce", nonce.size(), GCM_NONCE_BYTES)) return false;
|
||||
std::string body;
|
||||
if (!opHandle.updateCompletely(message, &body)) return false;
|
||||
|
||||
std::string mac;
|
||||
if (!opHandle.finish(&mac)) return false;
|
||||
if (!checkSize("mac", mac.size(), GCM_MAC_BYTES)) return false;
|
||||
*ciphertext = nonce + body + mac;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool decryptWithKeymasterKey(Keymaster& keymaster, const std::string& dir,
|
||||
const km::AuthorizationSet& keyParams,
|
||||
const km::HardwareAuthToken& authToken,
|
||||
const std::string& ciphertext, KeyBuffer* message) {
|
||||
auto nonce = ciphertext.substr(0, GCM_NONCE_BYTES);
|
||||
auto bodyAndMac = ciphertext.substr(GCM_NONCE_BYTES);
|
||||
auto opParams = km::AuthorizationSetBuilder().Authorization(km::TAG_NONCE,
|
||||
km::support::blob2hidlVec(nonce));
|
||||
auto opHandle =
|
||||
begin(keymaster, dir, km::KeyPurpose::DECRYPT, keyParams, opParams, authToken, nullptr);
|
||||
if (!opHandle) return false;
|
||||
if (!opHandle.updateCompletely(bodyAndMac, message)) return false;
|
||||
if (!opHandle.finish(nullptr)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static std::string getStretching(const KeyAuthentication& auth) {
|
||||
if (!auth.usesKeymaster()) {
|
||||
return kStretch_none;
|
||||
} else if (auth.secret.empty()) {
|
||||
return kStretch_nopassword;
|
||||
} else {
|
||||
char paramstr[PROPERTY_VALUE_MAX];
|
||||
|
||||
property_get(SCRYPT_PROP, paramstr, SCRYPT_DEFAULTS);
|
||||
return std::string() + kStretchPrefix_scrypt + paramstr;
|
||||
}
|
||||
}
|
||||
|
||||
static bool stretchingNeedsSalt(const std::string& stretching) {
|
||||
return stretching != kStretch_nopassword && stretching != kStretch_none;
|
||||
}
|
||||
|
||||
static bool stretchSecret(const std::string& stretching, const std::string& secret,
|
||||
const std::string& salt, std::string* stretched) {
|
||||
if (stretching == kStretch_nopassword) {
|
||||
if (!secret.empty()) {
|
||||
LOG(WARNING) << "Password present but stretching is nopassword" << std::endl;
|
||||
// Continue anyway
|
||||
}
|
||||
stretched->clear();
|
||||
} else if (stretching == kStretch_none) {
|
||||
*stretched = secret;
|
||||
} else if (std::equal(kStretchPrefix_scrypt.begin(), kStretchPrefix_scrypt.end(),
|
||||
stretching.begin())) {
|
||||
int Nf, rf, pf;
|
||||
if (!parse_scrypt_parameters(stretching.substr(kStretchPrefix_scrypt.size()).c_str(), &Nf,
|
||||
&rf, &pf)) {
|
||||
LOG(ERROR) << "Unable to parse scrypt params in stretching: " << stretching << std::endl;
|
||||
return false;
|
||||
}
|
||||
stretched->assign(STRETCHED_BYTES, '\0');
|
||||
if (crypto_scrypt(reinterpret_cast<const uint8_t*>(secret.data()), secret.size(),
|
||||
reinterpret_cast<const uint8_t*>(salt.data()), salt.size(), 1 << Nf,
|
||||
1 << rf, 1 << pf, reinterpret_cast<uint8_t*>(&(*stretched)[0]),
|
||||
stretched->size()) != 0) {
|
||||
LOG(ERROR) << "scrypt failed with params: " << stretching << std::endl;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
LOG(ERROR) << "Unknown stretching type: " << stretching << std::endl;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool generateAppId(const KeyAuthentication& auth, const std::string& stretching,
|
||||
const std::string& salt, const std::string& secdiscardable_hash,
|
||||
std::string* appId) {
|
||||
std::string stretched;
|
||||
if (!stretchSecret(stretching, auth.secret, salt, &stretched)) return false;
|
||||
*appId = secdiscardable_hash + stretched;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void logOpensslError() {
|
||||
LOG(ERROR) << "Openssl error: " << ERR_get_error() << std::endl;
|
||||
}
|
||||
|
||||
static bool encryptWithoutKeymaster(const std::string& preKey, const KeyBuffer& plaintext,
|
||||
std::string* ciphertext) {
|
||||
std::string key;
|
||||
hashWithPrefix(kHashPrefix_keygen, preKey, &key);
|
||||
key.resize(AES_KEY_BYTES);
|
||||
if (!readRandomBytesOrLog(GCM_NONCE_BYTES, ciphertext)) return false;
|
||||
auto ctx = std::unique_ptr<EVP_CIPHER_CTX, decltype(&::EVP_CIPHER_CTX_free)>(
|
||||
EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free);
|
||||
if (!ctx) {
|
||||
logOpensslError();
|
||||
return false;
|
||||
}
|
||||
if (1 != EVP_EncryptInit_ex(ctx.get(), EVP_aes_256_gcm(), NULL,
|
||||
reinterpret_cast<const uint8_t*>(key.data()),
|
||||
reinterpret_cast<const uint8_t*>(ciphertext->data()))) {
|
||||
logOpensslError();
|
||||
return false;
|
||||
}
|
||||
ciphertext->resize(GCM_NONCE_BYTES + plaintext.size() + GCM_MAC_BYTES);
|
||||
int outlen;
|
||||
if (1 != EVP_EncryptUpdate(
|
||||
ctx.get(), reinterpret_cast<uint8_t*>(&(*ciphertext)[0] + GCM_NONCE_BYTES),
|
||||
&outlen, reinterpret_cast<const uint8_t*>(plaintext.data()), plaintext.size())) {
|
||||
logOpensslError();
|
||||
return false;
|
||||
}
|
||||
if (outlen != static_cast<int>(plaintext.size())) {
|
||||
LOG(ERROR) << "GCM ciphertext length should be " << plaintext.size() << " was " << outlen << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (1 != EVP_EncryptFinal_ex(
|
||||
ctx.get(),
|
||||
reinterpret_cast<uint8_t*>(&(*ciphertext)[0] + GCM_NONCE_BYTES + plaintext.size()),
|
||||
&outlen)) {
|
||||
logOpensslError();
|
||||
return false;
|
||||
}
|
||||
if (outlen != 0) {
|
||||
LOG(ERROR) << "GCM EncryptFinal should be 0, was " << outlen << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (1 != EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_GET_TAG, GCM_MAC_BYTES,
|
||||
reinterpret_cast<uint8_t*>(&(*ciphertext)[0] + GCM_NONCE_BYTES +
|
||||
plaintext.size()))) {
|
||||
logOpensslError();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool decryptWithoutKeymaster(const std::string& preKey, const std::string& ciphertext,
|
||||
KeyBuffer* plaintext) {
|
||||
if (ciphertext.size() < GCM_NONCE_BYTES + GCM_MAC_BYTES) {
|
||||
LOG(ERROR) << "GCM ciphertext too small: " << ciphertext.size() << std::endl;
|
||||
return false;
|
||||
}
|
||||
std::string key;
|
||||
hashWithPrefix(kHashPrefix_keygen, preKey, &key);
|
||||
key.resize(AES_KEY_BYTES);
|
||||
auto ctx = std::unique_ptr<EVP_CIPHER_CTX, decltype(&::EVP_CIPHER_CTX_free)>(
|
||||
EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free);
|
||||
if (!ctx) {
|
||||
logOpensslError();
|
||||
return false;
|
||||
}
|
||||
if (1 != EVP_DecryptInit_ex(ctx.get(), EVP_aes_256_gcm(), NULL,
|
||||
reinterpret_cast<const uint8_t*>(key.data()),
|
||||
reinterpret_cast<const uint8_t*>(ciphertext.data()))) {
|
||||
logOpensslError();
|
||||
return false;
|
||||
}
|
||||
*plaintext = KeyBuffer(ciphertext.size() - GCM_NONCE_BYTES - GCM_MAC_BYTES);
|
||||
int outlen;
|
||||
if (1 != EVP_DecryptUpdate(ctx.get(), reinterpret_cast<uint8_t*>(&(*plaintext)[0]), &outlen,
|
||||
reinterpret_cast<const uint8_t*>(ciphertext.data() + GCM_NONCE_BYTES),
|
||||
plaintext->size())) {
|
||||
logOpensslError();
|
||||
return false;
|
||||
}
|
||||
if (outlen != static_cast<int>(plaintext->size())) {
|
||||
LOG(ERROR) << "GCM plaintext length should be " << plaintext->size() << " was " << outlen << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (1 != EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_SET_TAG, GCM_MAC_BYTES,
|
||||
const_cast<void*>(reinterpret_cast<const void*>(
|
||||
ciphertext.data() + GCM_NONCE_BYTES + plaintext->size())))) {
|
||||
logOpensslError();
|
||||
return false;
|
||||
}
|
||||
if (1 != EVP_DecryptFinal_ex(ctx.get(),
|
||||
reinterpret_cast<uint8_t*>(&(*plaintext)[0] + plaintext->size()),
|
||||
&outlen)) {
|
||||
logOpensslError();
|
||||
return false;
|
||||
}
|
||||
if (outlen != 0) {
|
||||
LOG(ERROR) << "GCM EncryptFinal should be 0, was " << outlen << std::endl;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool pathExists(const std::string& path) {
|
||||
return access(path.c_str(), F_OK) == 0;
|
||||
}
|
||||
|
||||
bool storeKey(const std::string& dir, const KeyAuthentication& auth, const KeyBuffer& key) {
|
||||
if (TEMP_FAILURE_RETRY(mkdir(dir.c_str(), 0700)) == -1) {
|
||||
PLOG(ERROR) << "key mkdir " << dir << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (!writeStringToFile(kCurrentVersion, dir + "/" + kFn_version)) return false;
|
||||
std::string secdiscardable_hash;
|
||||
if (!createSecdiscardable(dir + "/" + kFn_secdiscardable, &secdiscardable_hash)) return false;
|
||||
std::string stretching = getStretching(auth);
|
||||
if (!writeStringToFile(stretching, dir + "/" + kFn_stretching)) return false;
|
||||
std::string salt;
|
||||
if (stretchingNeedsSalt(stretching)) {
|
||||
if (ReadRandomBytes(SALT_BYTES, salt) != OK) {
|
||||
LOG(ERROR) << "Random read failed" << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (!writeStringToFile(salt, dir + "/" + kFn_salt)) return false;
|
||||
}
|
||||
std::string appId;
|
||||
if (!generateAppId(auth, stretching, salt, secdiscardable_hash, &appId)) return false;
|
||||
std::string encryptedKey;
|
||||
if (auth.usesKeymaster()) {
|
||||
Keymaster keymaster;
|
||||
if (!keymaster) return false;
|
||||
std::string kmKey;
|
||||
if (!generateKeymasterKey(keymaster, auth, appId, &kmKey)) return false;
|
||||
if (!writeStringToFile(kmKey, dir + "/" + kFn_keymaster_key_blob)) return false;
|
||||
km::AuthorizationSet keyParams;
|
||||
km::HardwareAuthToken authToken;
|
||||
std::tie(keyParams, authToken) = beginParams(auth, appId);
|
||||
if (!encryptWithKeymasterKey(keymaster, dir, keyParams, authToken, key, &encryptedKey))
|
||||
return false;
|
||||
} else {
|
||||
if (!encryptWithoutKeymaster(appId, key, &encryptedKey)) return false;
|
||||
}
|
||||
if (!writeStringToFile(encryptedKey, dir + "/" + kFn_encrypted_key)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool storeKeyAtomically(const std::string& key_path, const std::string& tmp_path,
|
||||
const KeyAuthentication& auth, const KeyBuffer& key) {
|
||||
if (pathExists(key_path)) {
|
||||
LOG(ERROR) << "Already exists, cannot create key at: " << key_path << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (pathExists(tmp_path)) {
|
||||
LOG(DEBUG) << "Already exists, destroying: " << tmp_path << std::endl;
|
||||
destroyKey(tmp_path); // May be partially created so ignore errors
|
||||
}
|
||||
if (!storeKey(tmp_path, auth, key)) return false;
|
||||
if (rename(tmp_path.c_str(), key_path.c_str()) != 0) {
|
||||
PLOG(ERROR) << "Unable to move new key to location: " << key_path << std::endl;
|
||||
return false;
|
||||
}
|
||||
LOG(DEBUG) << "Created key: " << key_path << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool retrieveKey(const std::string& dir, const KeyAuthentication& auth, KeyBuffer* key) {
|
||||
std::string version;
|
||||
if (!readFileToString(dir + "/" + kFn_version, &version)) return false;
|
||||
if (version != kCurrentVersion) {
|
||||
LOG(ERROR) << "Version mismatch, expected " << kCurrentVersion << " got " << version << std::endl;
|
||||
return false;
|
||||
}
|
||||
std::string secdiscardable_hash;
|
||||
if (!readSecdiscardable(dir + "/" + kFn_secdiscardable, &secdiscardable_hash)) return false;
|
||||
std::string stretching;
|
||||
if (!readFileToString(dir + "/" + kFn_stretching, &stretching)) return false;
|
||||
std::string salt;
|
||||
if (stretchingNeedsSalt(stretching)) {
|
||||
if (!readFileToString(dir + "/" + kFn_salt, &salt)) return false;
|
||||
}
|
||||
std::string appId;
|
||||
if (!generateAppId(auth, stretching, salt, secdiscardable_hash, &appId)) return false;
|
||||
std::string encryptedMessage;
|
||||
if (!readFileToString(dir + "/" + kFn_encrypted_key, &encryptedMessage)) return false;
|
||||
if (auth.usesKeymaster()) {
|
||||
Keymaster keymaster;
|
||||
if (!keymaster) return false;
|
||||
km::AuthorizationSet keyParams;
|
||||
km::HardwareAuthToken authToken;
|
||||
std::tie(keyParams, authToken) = beginParams(auth, appId);
|
||||
if (!decryptWithKeymasterKey(keymaster, dir, keyParams, authToken, encryptedMessage, key))
|
||||
return false;
|
||||
} else {
|
||||
if (!decryptWithoutKeymaster(appId, encryptedMessage, key)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool deleteKey(const std::string& dir) {
|
||||
LOG(DEBUG) << "not deleting key in " << __FILE__ << std::endl;
|
||||
return true;
|
||||
std::string kmKey;
|
||||
if (!readFileToString(dir + "/" + kFn_keymaster_key_blob, &kmKey)) return false;
|
||||
Keymaster keymaster;
|
||||
if (!keymaster) return false;
|
||||
if (!keymaster.deleteKey(kmKey)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool runSecdiscardSingle(const std::string& file) {
|
||||
if (ForkExecvp(std::vector<std::string>{kSecdiscardPath, "--", file}) != 0) {
|
||||
LOG(ERROR) << "secdiscard failed" << std::endl;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool recursiveDeleteKey(const std::string& dir) {
|
||||
LOG(DEBUG) << "not recursively deleting key in " << __FILE__ << std::endl;
|
||||
return true;
|
||||
if (ForkExecvp(std::vector<std::string>{kRmPath, "-rf", dir}) != 0) {
|
||||
LOG(ERROR) << "recursive delete failed" << std::endl;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool destroyKey(const std::string& dir) {
|
||||
LOG(DEBUG) << "not destroying key in " << __FILE__ << std::endl;
|
||||
return true;
|
||||
bool success = true;
|
||||
// Try each thing, even if previous things failed.
|
||||
bool uses_km = pathExists(dir + "/" + kFn_keymaster_key_blob);
|
||||
if (uses_km) {
|
||||
success &= deleteKey(dir);
|
||||
}
|
||||
auto secdiscard_cmd = std::vector<std::string>{
|
||||
kSecdiscardPath, "--", dir + "/" + kFn_encrypted_key, dir + "/" + kFn_secdiscardable,
|
||||
};
|
||||
if (uses_km) {
|
||||
secdiscard_cmd.emplace_back(dir + "/" + kFn_keymaster_key_blob);
|
||||
}
|
||||
if (ForkExecvp(secdiscard_cmd) != 0) {
|
||||
LOG(ERROR) << "secdiscard failed" << std::endl;
|
||||
success = false;
|
||||
}
|
||||
success &= recursiveDeleteKey(dir);
|
||||
return success;
|
||||
}
|
||||
|
||||
} // namespace vold
|
||||
} // namespace android
|
||||
73
crypto/ext4crypt/KeyStorage4.h
Normal file
@@ -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 <string>
|
||||
|
||||
namespace android {
|
||||
namespace vold {
|
||||
|
||||
// Represents the information needed to decrypt a disk encryption key.
|
||||
// If "token" is nonempty, it is passed in as a required Gatekeeper auth token.
|
||||
// If "token" and "secret" are nonempty, "secret" is appended to the application-specific
|
||||
// binary needed to unlock.
|
||||
// If only "secret" is nonempty, it is used to decrypt in a non-Keymaster process.
|
||||
class KeyAuthentication {
|
||||
public:
|
||||
KeyAuthentication(std::string t, std::string s) : token{t}, secret{s} {};
|
||||
|
||||
bool usesKeymaster() const { return !token.empty() || secret.empty(); };
|
||||
|
||||
const std::string token;
|
||||
const std::string secret;
|
||||
};
|
||||
|
||||
extern const KeyAuthentication kEmptyAuthentication;
|
||||
|
||||
// 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
|
||||
215
crypto/ext4crypt/KeyUtil.cpp
Normal file
@@ -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 <iomanip>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include <openssl/sha.h>
|
||||
|
||||
#include <android-base/file.h>
|
||||
//#include <android-base/logging.h>
|
||||
#include <keyutils.h>
|
||||
|
||||
#include "KeyStorage4.h"
|
||||
#include "Utils.h"
|
||||
|
||||
#include <iostream>
|
||||
#define LOG(x) std::cout
|
||||
#define PLOG(x) std::cout
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace android {
|
||||
namespace vold {
|
||||
|
||||
// ext4enc:TODO get this const from somewhere good
|
||||
const int EXT4_KEY_DESCRIPTOR_SIZE = 8;
|
||||
|
||||
// ext4enc:TODO Include structure from somewhere sensible
|
||||
// MUST be in sync with ext4_crypto.c in kernel
|
||||
constexpr int EXT4_ENCRYPTION_MODE_AES_256_XTS = 1;
|
||||
constexpr int EXT4_AES_256_XTS_KEY_SIZE = 64;
|
||||
constexpr int EXT4_MAX_KEY_SIZE = 64;
|
||||
struct ext4_encryption_key {
|
||||
uint32_t mode;
|
||||
char raw[EXT4_MAX_KEY_SIZE];
|
||||
uint32_t size;
|
||||
};
|
||||
|
||||
bool randomKey(KeyBuffer* key) {
|
||||
*key = KeyBuffer(EXT4_AES_256_XTS_KEY_SIZE);
|
||||
if (ReadRandomBytes(key->size(), key->data()) != 0) {
|
||||
// TODO status_t plays badly with PLOG, fix it.
|
||||
LOG(ERROR) << "Random read failed" << std::endl;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get raw keyref - used to make keyname and to pass to ioctl
|
||||
static std::string generateKeyRef(const char* key, int length) {
|
||||
SHA512_CTX c;
|
||||
|
||||
SHA512_Init(&c);
|
||||
SHA512_Update(&c, key, length);
|
||||
unsigned char key_ref1[SHA512_DIGEST_LENGTH];
|
||||
SHA512_Final(key_ref1, &c);
|
||||
|
||||
SHA512_Init(&c);
|
||||
SHA512_Update(&c, key_ref1, SHA512_DIGEST_LENGTH);
|
||||
unsigned char key_ref2[SHA512_DIGEST_LENGTH];
|
||||
SHA512_Final(key_ref2, &c);
|
||||
|
||||
static_assert(EXT4_KEY_DESCRIPTOR_SIZE <= SHA512_DIGEST_LENGTH,
|
||||
"Hash too short for descriptor");
|
||||
return std::string((char*)key_ref2, EXT4_KEY_DESCRIPTOR_SIZE);
|
||||
}
|
||||
|
||||
static bool fillKey(const KeyBuffer& key, ext4_encryption_key* ext4_key) {
|
||||
if (key.size() != EXT4_AES_256_XTS_KEY_SIZE) {
|
||||
LOG(ERROR) << "Wrong size key " << key.size();
|
||||
return false;
|
||||
}
|
||||
static_assert(EXT4_AES_256_XTS_KEY_SIZE <= sizeof(ext4_key->raw), "Key too long!");
|
||||
ext4_key->mode = EXT4_ENCRYPTION_MODE_AES_256_XTS;
|
||||
ext4_key->size = key.size();
|
||||
memset(ext4_key->raw, 0, sizeof(ext4_key->raw));
|
||||
memcpy(ext4_key->raw, key.data(), key.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
static char const* const NAME_PREFIXES[] = {
|
||||
"ext4",
|
||||
"f2fs",
|
||||
"fscrypt",
|
||||
nullptr
|
||||
};
|
||||
|
||||
static std::string keyname(const std::string& prefix, const std::string& raw_ref) {
|
||||
std::ostringstream o;
|
||||
o << prefix << ":";
|
||||
for (unsigned char i : raw_ref) {
|
||||
o << std::hex << std::setw(2) << std::setfill('0') << (int)i;
|
||||
}
|
||||
return o.str();
|
||||
}
|
||||
|
||||
// Get the keyring we store all keys in
|
||||
static bool e4cryptKeyring(key_serial_t* device_keyring) {
|
||||
*device_keyring = keyctl_search(KEY_SPEC_SESSION_KEYRING, "keyring", "e4crypt", 0);
|
||||
if (*device_keyring == -1) {
|
||||
PLOG(ERROR) << "Unable to find device keyring" << std::endl;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Install password into global keyring
|
||||
// Return raw key reference for use in policy
|
||||
bool installKey(const KeyBuffer& key, std::string* raw_ref) {
|
||||
// Place ext4_encryption_key into automatically zeroing buffer.
|
||||
KeyBuffer ext4KeyBuffer(sizeof(ext4_encryption_key));
|
||||
ext4_encryption_key &ext4_key = *reinterpret_cast<ext4_encryption_key*>(ext4KeyBuffer.data());
|
||||
|
||||
if (!fillKey(key, &ext4_key)) return false;
|
||||
*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
|
||||
41
crypto/ext4crypt/KeyUtil.h
Normal file
@@ -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 <string>
|
||||
#include <memory>
|
||||
|
||||
namespace android {
|
||||
namespace vold {
|
||||
|
||||
bool randomKey(KeyBuffer* key);
|
||||
bool installKey(const KeyBuffer& key, std::string* raw_ref);
|
||||
bool evictKey(const std::string& raw_ref);
|
||||
bool retrieveAndInstallKey(bool create_if_absent, const KeyAuthentication& key_authentication,
|
||||
const std::string& key_path, const std::string& tmp_path,
|
||||
std::string* key_ref);
|
||||
bool retrieveKey(bool create_if_absent, const std::string& key_path,
|
||||
const std::string& tmp_path, KeyBuffer* key);
|
||||
|
||||
} // namespace vold
|
||||
} // namespace android
|
||||
|
||||
#endif
|
||||
352
crypto/ext4crypt/Keymaster4.cpp
Normal file
@@ -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 <android-base/logging.h>
|
||||
#include <keymasterV4_0/authorization_set.h>
|
||||
#include <keymasterV4_0/keymaster_utils.h>
|
||||
|
||||
#include <iostream>
|
||||
#define LOG(x) std::cout
|
||||
#define PLOG(x) std::cout
|
||||
|
||||
namespace android {
|
||||
namespace vold {
|
||||
|
||||
using ::android::hardware::hidl_string;
|
||||
using ::android::hardware::hidl_vec;
|
||||
using ::android::hardware::keymaster::V4_0::SecurityLevel;
|
||||
|
||||
KeymasterOperation::~KeymasterOperation() {
|
||||
if (mDevice) mDevice->abort(mOpHandle);
|
||||
}
|
||||
|
||||
bool KeymasterOperation::updateCompletely(const char* input, size_t inputLen,
|
||||
const std::function<void(const char*, size_t)> consumer) {
|
||||
uint32_t inputConsumed = 0;
|
||||
|
||||
km::ErrorCode km_error;
|
||||
auto hidlCB = [&](km::ErrorCode ret, uint32_t inputConsumedDelta,
|
||||
const hidl_vec<km::KeyParameter>& /*ignored*/,
|
||||
const hidl_vec<uint8_t>& _output) {
|
||||
km_error = ret;
|
||||
if (km_error != km::ErrorCode::OK) return;
|
||||
inputConsumed += inputConsumedDelta;
|
||||
consumer(reinterpret_cast<const char*>(&_output[0]), _output.size());
|
||||
};
|
||||
|
||||
while (inputConsumed != inputLen) {
|
||||
size_t toRead = static_cast<size_t>(inputLen - inputConsumed);
|
||||
auto inputBlob = km::support::blob2hidlVec(
|
||||
reinterpret_cast<const uint8_t*>(&input[inputConsumed]), toRead);
|
||||
auto error = mDevice->update(mOpHandle, hidl_vec<km::KeyParameter>(), inputBlob,
|
||||
km::HardwareAuthToken(), km::VerificationToken(), hidlCB);
|
||||
if (!error.isOk()) {
|
||||
LOG(ERROR) << "update failed: " << error.description() << std::endl;
|
||||
mDevice = nullptr;
|
||||
return false;
|
||||
}
|
||||
if (km_error != km::ErrorCode::OK) {
|
||||
LOG(ERROR) << "update failed, code " << int32_t(km_error) << std::endl;
|
||||
mDevice = nullptr;
|
||||
return false;
|
||||
}
|
||||
if (inputConsumed > inputLen) {
|
||||
LOG(ERROR) << "update reported too much input consumed" << std::endl;
|
||||
mDevice = nullptr;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool KeymasterOperation::finish(std::string* output) {
|
||||
km::ErrorCode km_error;
|
||||
auto hidlCb = [&](km::ErrorCode ret, const hidl_vec<km::KeyParameter>& /*ignored*/,
|
||||
const hidl_vec<uint8_t>& _output) {
|
||||
km_error = ret;
|
||||
if (km_error != km::ErrorCode::OK) return;
|
||||
if (output) output->assign(reinterpret_cast<const char*>(&_output[0]), _output.size());
|
||||
};
|
||||
auto error = mDevice->finish(mOpHandle, hidl_vec<km::KeyParameter>(), hidl_vec<uint8_t>(),
|
||||
hidl_vec<uint8_t>(), km::HardwareAuthToken(),
|
||||
km::VerificationToken(), hidlCb);
|
||||
mDevice = nullptr;
|
||||
if (!error.isOk()) {
|
||||
LOG(ERROR) << "finish failed: " << error.description() << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (km_error != km::ErrorCode::OK) {
|
||||
LOG(ERROR) << "finish failed, code " << int32_t(km_error) << std::endl;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ bool Keymaster::hmacKeyGenerated = false;
|
||||
|
||||
Keymaster::Keymaster() {
|
||||
auto devices = KmDevice::enumerateAvailableDevices();
|
||||
if (!hmacKeyGenerated) {
|
||||
KmDevice::performHmacKeyAgreement(devices);
|
||||
hmacKeyGenerated = true;
|
||||
}
|
||||
for (auto& dev : devices) {
|
||||
// Do not use StrongBox for device encryption / credential encryption. If a security chip
|
||||
// is present it will have Weaver, which already strengthens CE. We get no additional
|
||||
// benefit from using StrongBox here, so skip it.
|
||||
if (dev->halVersion().securityLevel != SecurityLevel::STRONGBOX) {
|
||||
mDevice = std::move(dev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!mDevice) return;
|
||||
auto& version = mDevice->halVersion();
|
||||
LOG(INFO) << "Using " << version.keymasterName << " from " << version.authorName
|
||||
<< " for encryption. Security level: " << toString(version.securityLevel)
|
||||
<< ", HAL: " << mDevice->descriptor() << "/" << mDevice->instanceName() << std::endl;
|
||||
}
|
||||
|
||||
bool Keymaster::generateKey(const km::AuthorizationSet& inParams, std::string* key) {
|
||||
km::ErrorCode km_error;
|
||||
auto hidlCb = [&](km::ErrorCode ret, const hidl_vec<uint8_t>& keyBlob,
|
||||
const km::KeyCharacteristics& /*ignored*/) {
|
||||
km_error = ret;
|
||||
if (km_error != km::ErrorCode::OK) return;
|
||||
if (key) key->assign(reinterpret_cast<const char*>(&keyBlob[0]), keyBlob.size());
|
||||
};
|
||||
|
||||
auto error = mDevice->generateKey(inParams.hidl_data(), hidlCb);
|
||||
if (!error.isOk()) {
|
||||
LOG(ERROR) << "generate_key failed: " << error.description() << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (km_error != km::ErrorCode::OK) {
|
||||
LOG(ERROR) << "generate_key failed, code " << int32_t(km_error) << std::endl;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Keymaster::deleteKey(const std::string& key) {
|
||||
LOG(ERROR) << "not actually deleting key\n";
|
||||
return true;
|
||||
auto keyBlob = km::support::blob2hidlVec(key);
|
||||
auto error = mDevice->deleteKey(keyBlob);
|
||||
if (!error.isOk()) {
|
||||
LOG(ERROR) << "delete_key failed: " << error.description();
|
||||
return false;
|
||||
}
|
||||
if (error != km::ErrorCode::OK) {
|
||||
LOG(ERROR) << "delete_key failed, code " << int32_t(km::ErrorCode(error));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Keymaster::upgradeKey(const std::string& oldKey, const km::AuthorizationSet& inParams,
|
||||
std::string* newKey) {
|
||||
auto oldKeyBlob = km::support::blob2hidlVec(oldKey);
|
||||
km::ErrorCode km_error;
|
||||
auto hidlCb = [&](km::ErrorCode ret, const hidl_vec<uint8_t>& upgradedKeyBlob) {
|
||||
km_error = ret;
|
||||
if (km_error != km::ErrorCode::OK) return;
|
||||
if (newKey)
|
||||
newKey->assign(reinterpret_cast<const char*>(&upgradedKeyBlob[0]),
|
||||
upgradedKeyBlob.size());
|
||||
};
|
||||
auto error = mDevice->upgradeKey(oldKeyBlob, inParams.hidl_data(), hidlCb);
|
||||
if (!error.isOk()) {
|
||||
LOG(ERROR) << "upgrade_key failed: " << error.description() << std::endl;
|
||||
return false;
|
||||
}
|
||||
if (km_error != km::ErrorCode::OK) {
|
||||
LOG(ERROR) << "upgrade_key failed, code " << int32_t(km_error) << std::endl;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
KeymasterOperation Keymaster::begin(km::KeyPurpose purpose, const std::string& key,
|
||||
const km::AuthorizationSet& inParams,
|
||||
const km::HardwareAuthToken& authToken,
|
||||
km::AuthorizationSet* outParams) {
|
||||
auto keyBlob = km::support::blob2hidlVec(key);
|
||||
uint64_t mOpHandle;
|
||||
km::ErrorCode km_error;
|
||||
|
||||
auto hidlCb = [&](km::ErrorCode ret, const hidl_vec<km::KeyParameter>& _outParams,
|
||||
uint64_t operationHandle) {
|
||||
km_error = ret;
|
||||
if (km_error != km::ErrorCode::OK) return;
|
||||
if (outParams) *outParams = _outParams;
|
||||
mOpHandle = operationHandle;
|
||||
};
|
||||
|
||||
auto error = mDevice->begin(purpose, keyBlob, inParams.hidl_data(), authToken, hidlCb);
|
||||
if (!error.isOk()) {
|
||||
LOG(ERROR) << "begin failed: " << error.description() << std::endl;
|
||||
return KeymasterOperation(km::ErrorCode::UNKNOWN_ERROR);
|
||||
}
|
||||
if (km_error != km::ErrorCode::OK) {
|
||||
LOG(ERROR) << "begin failed, code " << int32_t(km_error) << std::endl;
|
||||
return KeymasterOperation(km_error);
|
||||
}
|
||||
return KeymasterOperation(mDevice.get(), mOpHandle);
|
||||
}
|
||||
|
||||
bool Keymaster::isSecure() {
|
||||
return mDevice->halVersion().securityLevel != km::SecurityLevel::SOFTWARE;
|
||||
}
|
||||
|
||||
} // namespace vold
|
||||
} // namespace android
|
||||
|
||||
using namespace ::android::vold;
|
||||
|
||||
int keymaster_compatibility_cryptfs_scrypt() {
|
||||
Keymaster dev;
|
||||
if (!dev) {
|
||||
LOG(ERROR) << "Failed to initiate keymaster session" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
return dev.isSecure();
|
||||
}
|
||||
|
||||
static bool write_string_to_buf(const std::string& towrite, uint8_t* buffer, uint32_t buffer_size,
|
||||
uint32_t* out_size) {
|
||||
if (!buffer || !out_size) {
|
||||
LOG(ERROR) << "Missing target pointers" << std::endl;
|
||||
return false;
|
||||
}
|
||||
*out_size = towrite.size();
|
||||
if (buffer_size < towrite.size()) {
|
||||
LOG(ERROR) << "Buffer too small " << buffer_size << " < " << towrite.size() << std::endl;
|
||||
return false;
|
||||
}
|
||||
memset(buffer, '\0', buffer_size);
|
||||
std::copy(towrite.begin(), towrite.end(), buffer);
|
||||
return true;
|
||||
}
|
||||
|
||||
static km::AuthorizationSet keyParams(uint32_t rsa_key_size, uint64_t rsa_exponent,
|
||||
uint32_t ratelimit) {
|
||||
return km::AuthorizationSetBuilder()
|
||||
.RsaSigningKey(rsa_key_size, rsa_exponent)
|
||||
.NoDigestOrPadding()
|
||||
.Authorization(km::TAG_BLOB_USAGE_REQUIREMENTS, km::KeyBlobUsageRequirements::STANDALONE)
|
||||
.Authorization(km::TAG_NO_AUTH_REQUIRED)
|
||||
.Authorization(km::TAG_MIN_SECONDS_BETWEEN_OPS, ratelimit);
|
||||
}
|
||||
|
||||
int keymaster_create_key_for_cryptfs_scrypt(uint32_t rsa_key_size, uint64_t rsa_exponent,
|
||||
uint32_t ratelimit, uint8_t* key_buffer,
|
||||
uint32_t key_buffer_size, uint32_t* key_out_size) {
|
||||
if (key_out_size) {
|
||||
*key_out_size = 0;
|
||||
}
|
||||
Keymaster dev;
|
||||
if (!dev) {
|
||||
LOG(ERROR) << "Failed to initiate keymaster session" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
std::string key;
|
||||
if (!dev.generateKey(keyParams(rsa_key_size, rsa_exponent, ratelimit), &key)) return -1;
|
||||
if (!write_string_to_buf(key, key_buffer, key_buffer_size, key_out_size)) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int keymaster_upgrade_key_for_cryptfs_scrypt(uint32_t rsa_key_size, uint64_t rsa_exponent,
|
||||
uint32_t ratelimit, const uint8_t* key_blob,
|
||||
size_t key_blob_size, uint8_t* key_buffer,
|
||||
uint32_t key_buffer_size, uint32_t* key_out_size) {
|
||||
if (key_out_size) {
|
||||
*key_out_size = 0;
|
||||
}
|
||||
Keymaster dev;
|
||||
if (!dev) {
|
||||
LOG(ERROR) << "Failed to initiate keymaster session" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
std::string old_key(reinterpret_cast<const char*>(key_blob), key_blob_size);
|
||||
std::string new_key;
|
||||
if (!dev.upgradeKey(old_key, keyParams(rsa_key_size, rsa_exponent, ratelimit), &new_key))
|
||||
return -1;
|
||||
if (!write_string_to_buf(new_key, key_buffer, key_buffer_size, key_out_size)) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
KeymasterSignResult keymaster_sign_object_for_cryptfs_scrypt(
|
||||
const uint8_t* key_blob, size_t key_blob_size, uint32_t ratelimit, const uint8_t* object,
|
||||
const size_t object_size, uint8_t** signature_buffer, size_t* signature_buffer_size) {
|
||||
Keymaster dev;
|
||||
if (!dev) {
|
||||
LOG(ERROR) << "Failed to initiate keymaster session" << std::endl;
|
||||
return KeymasterSignResult::error;
|
||||
}
|
||||
if (!key_blob || !object || !signature_buffer || !signature_buffer_size) {
|
||||
LOG(ERROR) << __FILE__ << ":" << __LINE__ << ":Invalid argument" << std::endl;
|
||||
return KeymasterSignResult::error;
|
||||
}
|
||||
|
||||
km::AuthorizationSet outParams;
|
||||
std::string key(reinterpret_cast<const char*>(key_blob), key_blob_size);
|
||||
std::string input(reinterpret_cast<const char*>(object), object_size);
|
||||
std::string output;
|
||||
KeymasterOperation op;
|
||||
|
||||
auto paramBuilder = km::AuthorizationSetBuilder().NoDigestOrPadding();
|
||||
while (true) {
|
||||
op = dev.begin(km::KeyPurpose::SIGN, key, paramBuilder, km::HardwareAuthToken(), &outParams);
|
||||
if (op.errorCode() == km::ErrorCode::KEY_RATE_LIMIT_EXCEEDED) {
|
||||
sleep(ratelimit);
|
||||
continue;
|
||||
} else
|
||||
break;
|
||||
}
|
||||
|
||||
if (op.errorCode() == km::ErrorCode::KEY_REQUIRES_UPGRADE) {
|
||||
LOG(ERROR) << "Keymaster key requires upgrade" << std::endl;
|
||||
return KeymasterSignResult::upgrade;
|
||||
}
|
||||
|
||||
if (op.errorCode() != km::ErrorCode::OK) {
|
||||
LOG(ERROR) << "Error starting keymaster signature transaction: " << int32_t(op.errorCode()) << std::endl;
|
||||
return KeymasterSignResult::error;
|
||||
}
|
||||
|
||||
if (!op.updateCompletely(input, &output)) {
|
||||
LOG(ERROR) << "Error sending data to keymaster signature transaction: "
|
||||
<< uint32_t(op.errorCode()) << std::endl;
|
||||
return KeymasterSignResult::error;
|
||||
}
|
||||
|
||||
if (!op.finish(&output)) {
|
||||
LOG(ERROR) << "Error finalizing keymaster signature transaction: "
|
||||
<< int32_t(op.errorCode()) << std::endl;
|
||||
return KeymasterSignResult::error;
|
||||
}
|
||||
|
||||
*signature_buffer = reinterpret_cast<uint8_t*>(malloc(output.size()));
|
||||
if (*signature_buffer == nullptr) {
|
||||
LOG(ERROR) << "Error allocation buffer for keymaster signature" << std::endl;
|
||||
return KeymasterSignResult::error;
|
||||
}
|
||||
*signature_buffer_size = output.size();
|
||||
std::copy(output.data(), output.data() + output.size(), *signature_buffer);
|
||||
return KeymasterSignResult::ok;
|
||||
}
|
||||
159
crypto/ext4crypt/Keymaster4.h
Normal file
@@ -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 <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include <android-base/macros.h>
|
||||
#include <keymasterV4_0/Keymaster.h>
|
||||
#include <keymasterV4_0/authorization_set.h>
|
||||
|
||||
namespace android {
|
||||
namespace vold {
|
||||
|
||||
namespace km = ::android::hardware::keymaster::V4_0;
|
||||
using KmDevice = km::support::Keymaster;
|
||||
|
||||
// C++ wrappers to the Keymaster hidl interface.
|
||||
// This is tailored to the needs of KeyStorage, but could be extended to be
|
||||
// a more general interface.
|
||||
|
||||
// Wrapper for a Keymaster operation handle representing an
|
||||
// ongoing Keymaster operation. Aborts the operation
|
||||
// in the destructor if it is unfinished. Methods log failures
|
||||
// to LOG(ERROR).
|
||||
class KeymasterOperation {
|
||||
public:
|
||||
~KeymasterOperation();
|
||||
// Is this instance valid? This is false if creation fails, and becomes
|
||||
// false on finish or if an update fails.
|
||||
explicit operator bool() { return mError == km::ErrorCode::OK; }
|
||||
km::ErrorCode errorCode() { return mError; }
|
||||
// Call "update" repeatedly until all of the input is consumed, and
|
||||
// concatenate the output. Return true on success.
|
||||
template <class TI, class TO>
|
||||
bool updateCompletely(TI& input, TO* output) {
|
||||
if (output) output->clear();
|
||||
return updateCompletely(input.data(), input.size(), [&](const char* b, size_t n) {
|
||||
if (output) std::copy(b, b + n, std::back_inserter(*output));
|
||||
});
|
||||
}
|
||||
|
||||
// Finish and write the output to this string, unless pointer is null.
|
||||
bool finish(std::string* output);
|
||||
// Move constructor
|
||||
KeymasterOperation(KeymasterOperation&& rhs) { *this = std::move(rhs); }
|
||||
// Construct an object in an error state for error returns
|
||||
KeymasterOperation() : mDevice{nullptr}, mOpHandle{0}, mError{km::ErrorCode::UNKNOWN_ERROR} {}
|
||||
// Move Assignment
|
||||
KeymasterOperation& operator=(KeymasterOperation&& rhs) {
|
||||
mDevice = rhs.mDevice;
|
||||
rhs.mDevice = nullptr;
|
||||
|
||||
mOpHandle = rhs.mOpHandle;
|
||||
rhs.mOpHandle = 0;
|
||||
|
||||
mError = rhs.mError;
|
||||
rhs.mError = km::ErrorCode::UNKNOWN_ERROR;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
KeymasterOperation(KmDevice* d, uint64_t h)
|
||||
: mDevice{d}, mOpHandle{h}, mError{km::ErrorCode::OK} {}
|
||||
KeymasterOperation(km::ErrorCode error) : mDevice{nullptr}, mOpHandle{0}, mError{error} {}
|
||||
|
||||
bool updateCompletely(const char* input, size_t inputLen,
|
||||
const std::function<void(const char*, size_t)> consumer);
|
||||
|
||||
KmDevice* mDevice;
|
||||
uint64_t mOpHandle;
|
||||
km::ErrorCode mError;
|
||||
DISALLOW_COPY_AND_ASSIGN(KeymasterOperation);
|
||||
friend class Keymaster;
|
||||
};
|
||||
|
||||
// Wrapper for a Keymaster device for methods that start a KeymasterOperation or are not
|
||||
// part of one.
|
||||
class Keymaster {
|
||||
public:
|
||||
Keymaster();
|
||||
// false if we failed to open the keymaster device.
|
||||
explicit operator bool() { return mDevice.get() != nullptr; }
|
||||
// Generate a key in the keymaster from the given params.
|
||||
bool generateKey(const km::AuthorizationSet& inParams, std::string* key);
|
||||
// If the keymaster supports it, permanently delete a key.
|
||||
bool deleteKey(const std::string& key);
|
||||
// Replace stored key blob in response to KM_ERROR_KEY_REQUIRES_UPGRADE.
|
||||
bool upgradeKey(const std::string& oldKey, const km::AuthorizationSet& inParams,
|
||||
std::string* newKey);
|
||||
// Begin a new cryptographic operation, collecting output parameters if pointer is non-null
|
||||
KeymasterOperation begin(km::KeyPurpose purpose, const std::string& key,
|
||||
const km::AuthorizationSet& inParams,
|
||||
const km::HardwareAuthToken& authToken,
|
||||
km::AuthorizationSet* outParams);
|
||||
bool isSecure();
|
||||
|
||||
private:
|
||||
std::unique_ptr<KmDevice> mDevice;
|
||||
DISALLOW_COPY_AND_ASSIGN(Keymaster);
|
||||
static bool hmacKeyGenerated;
|
||||
};
|
||||
|
||||
} // namespace vold
|
||||
} // namespace android
|
||||
|
||||
// FIXME no longer needed now cryptfs is in C++.
|
||||
|
||||
/*
|
||||
* The following functions provide C bindings to keymaster services
|
||||
* needed by cryptfs scrypt. The compatibility check checks whether
|
||||
* the keymaster implementation is considered secure, i.e., TEE backed.
|
||||
* The create_key function generates an RSA key for signing.
|
||||
* The sign_object function signes an object with the given keymaster
|
||||
* key.
|
||||
*/
|
||||
|
||||
/* Return values for keymaster_sign_object_for_cryptfs_scrypt */
|
||||
|
||||
enum class KeymasterSignResult {
|
||||
ok = 0,
|
||||
error = -1,
|
||||
upgrade = -2,
|
||||
};
|
||||
|
||||
int keymaster_compatibility_cryptfs_scrypt();
|
||||
int keymaster_create_key_for_cryptfs_scrypt(uint32_t rsa_key_size, uint64_t rsa_exponent,
|
||||
uint32_t ratelimit, uint8_t* key_buffer,
|
||||
uint32_t key_buffer_size, uint32_t* key_out_size);
|
||||
|
||||
int keymaster_upgrade_key_for_cryptfs_scrypt(uint32_t rsa_key_size, uint64_t rsa_exponent,
|
||||
uint32_t ratelimit, const uint8_t* key_blob,
|
||||
size_t key_blob_size, uint8_t* key_buffer,
|
||||
uint32_t key_buffer_size, uint32_t* key_out_size);
|
||||
|
||||
KeymasterSignResult keymaster_sign_object_for_cryptfs_scrypt(
|
||||
const uint8_t* key_blob, size_t key_blob_size, uint32_t ratelimit, const uint8_t* object,
|
||||
const size_t object_size, uint8_t** signature_buffer, size_t* signature_buffer_size);
|
||||
|
||||
#endif
|
||||
@@ -56,13 +56,13 @@ status_t ForkExecvp(const std::vector<std::string>& 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<std::string>& 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<std::string>& 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<std::string>& 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<std::string>& 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);
|
||||
|
||||
@@ -47,6 +47,7 @@ status_t ForkExecvp(const std::vector<std::string>& args,
|
||||
pid_t ForkExecvpAsync(const std::vector<std::string>& args);
|
||||
|
||||
status_t ReadRandomBytes(size_t bytes, std::string& out);
|
||||
status_t ReadRandomBytes(size_t bytes, char* buffer);
|
||||
|
||||
/* Converts hex string to raw bytes, ignoring [ :-] */
|
||||
status_t HexToStr(const std::string& hex, std::string& str);
|
||||
@@ -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);
|
||||
|
||||
@@ -24,9 +24,9 @@
|
||||
#include "Weaver1.h"
|
||||
|
||||
//#include <android-base/logging.h>
|
||||
#include <keystore/keymaster_tags.h>
|
||||
#include <keystore/authorization_set.h>
|
||||
#include <keystore/keystore_hidl_support.h>
|
||||
//#include <keystore/keymaster_tags.h>
|
||||
//#include <keystore/authorization_set.h>
|
||||
//#include <keystore/keystore_hidl_support.h>
|
||||
|
||||
#include <android/hardware/weaver/1.0/IWeaver.h>
|
||||
|
||||
|
||||
@@ -26,14 +26,20 @@
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
|
||||
#ifdef USE_SECURITY_NAMESPACE
|
||||
#include <android/security/IKeystoreService.h>
|
||||
#else
|
||||
#include <keystore/IKeystoreService.h>
|
||||
#include <keystore/authorization_set.h>
|
||||
#endif
|
||||
#include <binder/IPCThreadState.h>
|
||||
#include <binder/IServiceManager.h>
|
||||
|
||||
#include <keystore/keystore.h>
|
||||
#include <keystore/authorization_set.h>
|
||||
|
||||
#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<IServiceManager> sm = defaultServiceManager();
|
||||
sp<IBinder> binder = sm->getService(String16("android.security.keystore"));
|
||||
#ifdef USE_SECURITY_NAMESPACE
|
||||
sp<security::IKeystoreService> service = interface_cast<security::IKeystoreService>(binder);
|
||||
#else
|
||||
sp<IKeystoreService> service = interface_cast<IKeystoreService>(binder);
|
||||
#endif
|
||||
if (service == NULL) {
|
||||
printf("error: could not connect to keystore service\n");
|
||||
ALOGE("error: could not connect to keystore service\n");
|
||||
create_error_file();
|
||||
return -2;
|
||||
}
|
||||
#ifdef USE_SECURITY_NAMESPACE
|
||||
std::vector<uint8_t> auth_token_vector(&auth_token[0], (&auth_token[0]) + size);
|
||||
int result = 0;
|
||||
auto binder_result = service->addAuthToken(auth_token_vector, &result);
|
||||
if (!binder_result.isOk() || !keystore::KeyStoreServiceReturnCode(result).isOk()) {
|
||||
#else
|
||||
::keystore::KeyStoreServiceReturnCode auth_result = service->addAuthToken(auth_token, size);
|
||||
if (!auth_result.isOk()) {
|
||||
#endif
|
||||
// The keystore checks the uid of the calling process and will return a permission denied on this operation for user 0
|
||||
printf("keystore error adding auth token\n");
|
||||
ALOGE("keystore error adding auth token\n");
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 112 KiB |
|
Before Width: | Height: | Size: 103 KiB |
|
Before Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 69 KiB |
|
Before Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 73 KiB |
|
Before Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 196 KiB |
|
Before Width: | Height: | Size: 174 KiB |
|
Before Width: | Height: | Size: 86 KiB |
|
Before Width: | Height: | Size: 120 KiB |
|
Before Width: | Height: | Size: 85 KiB |
|
Before Width: | Height: | Size: 442 KiB |
|
Before Width: | Height: | Size: 410 KiB |
|
Before Width: | Height: | Size: 242 KiB |
|
Before Width: | Height: | Size: 261 KiB |
|
Before Width: | Height: | Size: 177 KiB |
|
Before Width: | Height: | Size: 604 KiB |
|
Before Width: | Height: | Size: 561 KiB |
|
Before Width: | Height: | Size: 329 KiB |