FsCrypt update: support fscrypt policies v1 and v2

This patchset introduces support decryption for Android 11.

In this update we deprecate ext4crypt. To specify the
policy version to use, use TW_USE_FSCRYPT_POLICY := 1 or
TW_USE_FSCRYPT_POLICY := 2. By default policy version will
be set to 2 if this variable is omitted.

Change-Id: I62a29c1bef36c259ec4b11259f71be613d20a112
This commit is contained in:
bigbiff
2021-03-07 18:20:29 -05:00
parent 9c1709b963
commit a957f078be
89 changed files with 6057 additions and 9395 deletions
+7 -6
View File
@@ -100,7 +100,6 @@ LOCAL_CLANG := true
LOCAL_C_INCLUDES += \
bionic \
system/vold \
system/extras \
system/core/adb \
system/core/libsparse \
@@ -287,10 +286,10 @@ ifeq ($(TW_INCLUDE_L_CRYPTO), true)
endif
ifeq ($(TW_INCLUDE_CRYPTO), true)
LOCAL_CFLAGS += -DTW_INCLUDE_CRYPTO -DUSE_FSCRYPT -Wno-macro-redefined
# LOCAL_SHARED_LIBRARIES += libcryptfsfde
LOCAL_SHARED_LIBRARIES += libgpt_twrp
LOCAL_C_INCLUDES += external/boringssl/src/include bootable/recovery/crypto/fscrypt \
bootable/recovery/crypto
LOCAL_SHARED_LIBRARIES += libcryptfsfde
LOCAL_SHARED_LIBRARIES += libgpt_twrp libstatssocket.recovery
LOCAL_C_INCLUDES += external/boringssl/src/include bootable/recovery/crypto
LOCAL_C_INCLUDES += $(commands_TWRP_local_path)/crypto/fscrypt
TW_INCLUDE_CRYPTO_FBE := true
LOCAL_CFLAGS += -DTW_INCLUDE_FBE
LOCAL_SHARED_LIBRARIES += libtwrpfscrypt android.frameworks.stats@1.0 android.hardware.authsecret@1.0 \
@@ -375,6 +374,8 @@ ifeq ($(TW_EXCLUDE_NANO), true)
LOCAL_CFLAGS += -DTW_EXCLUDE_NANO
endif
LOCAL_C_INCLUDES += system/vold \
TWRP_REQUIRED_MODULES += \
relink_libraries \
relink_binaries \
@@ -638,7 +639,7 @@ ifneq ($(TW_OZIP_DECRYPT_KEY),)
endif
ifeq ($(TW_INCLUDE_CRYPTO), true)
# include $(commands_TWRP_local_path)/crypto/fde/Android.mk
include $(commands_TWRP_local_path)/crypto/fde/Android.mk
include $(commands_TWRP_local_path)/crypto/scrypt/Android.mk
ifeq ($(TW_INCLUDE_CRYPTO_FBE), true)
include $(commands_TWRP_local_path)/crypto/fscrypt/Android.mk
-111
View File
@@ -1,111 +0,0 @@
LOCAL_PATH := $(call my-dir)
ifeq ($(TW_INCLUDE_CRYPTO), true)
include $(CLEAR_VARS)
LOCAL_MODULE := libe4crypt
LOCAL_MODULE_TAGS := optional
LOCAL_CFLAGS :=
LOCAL_SRC_FILES := Decrypt.cpp ScryptParameters.cpp Utils.cpp HashPassword.cpp ext4_crypt.cpp
LOCAL_SHARED_LIBRARIES := libselinux libc libc++ libext4_utils libbase libcrypto libcutils libkeymaster_messages libhardware libprotobuf-cpp-lite
LOCAL_STATIC_LIBRARIES := libscrypt_static
LOCAL_C_INCLUDES := system/extras/ext4_utils \
system/extras/ext4_utils/include/ext4_utils \
external/scrypt/lib/crypto \
system/security/keystore/include \
hardware/libhardware/include/hardware \
system/security/softkeymaster/include/keymaster \
system/keymaster/include
ifneq ($(wildcard hardware/libhardware/include/hardware/keymaster0.h),)
LOCAL_CFLAGS += -DTW_CRYPTO_HAVE_KEYMASTERX
LOCAL_C_INCLUDES += external/boringssl/src/include
endif
ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 26; echo $$?),0)
#8.0 or higher
LOCAL_CFLAGS += -DHAVE_GATEKEEPER1
ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 29; echo $$?),0)
LOCAL_SHARED_LIBRARIES += android.hardware.confirmationui@1.0
endif
LOCAL_SHARED_LIBRARIES += android.hardware.keymaster@3.0 libkeystore_binder libhidlbase libutils libbinder android.hardware.gatekeeper@1.0
ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 28; echo $$?),0)
#9.0 rules
LOCAL_CFLAGS += -DUSE_KEYSTORAGE_4 -Wno-unused-variable -Wno-sign-compare -Wno-unused-parameter -Wno-comment
LOCAL_SRC_FILES += Ext4CryptPie.cpp Keymaster4.cpp KeyStorage4.cpp KeyUtil.cpp MetadataCrypt.cpp KeyBuffer.cpp
LOCAL_SHARED_LIBRARIES += android.hardware.keymaster@4.0 libkeymaster4support
LOCAL_SHARED_LIBRARIES += android.hardware.gatekeeper@1.0 libkeystore_parcelables libkeystore_aidl
LOCAL_CFLAGS += -DHAVE_SYNTH_PWD_SUPPORT
LOCAL_SRC_FILES += Weaver1.cpp
LOCAL_SHARED_LIBRARIES += android.hardware.weaver@1.0
LOCAL_CFLAGS += -DHAVE_LIBKEYUTILS
LOCAL_SHARED_LIBRARIES += libkeyutils
else
#8.0 rules
LOCAL_CFLAGS += -DUSE_KEYSTORAGE_3
LOCAL_SRC_FILES += Ext4Crypt.cpp Keymaster3.cpp KeyStorage3.cpp
ifneq ($(wildcard hardware/interfaces/weaver/Android.bp),)
#only present in some 8.0 trees and should be in all 8.1 trees
LOCAL_CFLAGS += -DHAVE_SYNTH_PWD_SUPPORT
LOCAL_SRC_FILES += Weaver1.cpp
LOCAL_SHARED_LIBRARIES += android.hardware.weaver@1.0
endif
ifneq ($(wildcard system/core/libkeyutils/Android.bp),)
#only present in some 8.0 trees and should be in all 8.1 trees
LOCAL_CFLAGS += -DHAVE_LIBKEYUTILS
LOCAL_SHARED_LIBRARIES += libkeyutils
endif
endif
ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 28; echo $$?),0)
LOCAL_REQUIRED_MODULES := keystore_auth
else
LOCAL_ADDITIONAL_DEPENDENCIES := keystore_auth
endif
else
#7.x rules
LOCAL_SRC_FILES += Ext4Crypt.cpp Keymaster.cpp KeyStorage.cpp
endif
ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 28; echo $$?),0)
LOCAL_SHARED_LIBRARIES += libsoftkeymaster
endif
include $(BUILD_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := twrpfbe
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/
LOCAL_SRC_FILES := main.cpp
LOCAL_SHARED_LIBRARIES := libe4crypt
include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
LOCAL_MODULE := e4policyget
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/
LOCAL_SRC_FILES := e4policyget.cpp
LOCAL_SHARED_LIBRARIES := libe4crypt
LOCAL_LDFLAGS += -Wl,-dynamic-linker,/system/bin/linker64
include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
LOCAL_MODULE := keystore_auth
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/
LOCAL_SRC_FILES := keystore_auth.cpp
LOCAL_SHARED_LIBRARIES := libc libkeystore_binder libutils libbinder liblog
ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 28; echo $$?),0)
#9.0
LOCAL_CFLAGS += -DUSE_SECURITY_NAMESPACE
LOCAL_SHARED_LIBRARIES += libkeystore_aidl
endif
LOCAL_LDFLAGS += -Wl,-dynamic-linker,/system/bin/linker64
include $(BUILD_EXECUTABLE)
endif
File diff suppressed because it is too large Load Diff
-38
View File
@@ -1,38 +0,0 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdbool.h>
#include <sys/cdefs.h>
#include <cutils/multiuser.h>
#include <string>
__BEGIN_DECLS
// NOTE: keep in sync with StorageManager
static constexpr int FLAG_STORAGE_DE = 1 << 0;
static constexpr int FLAG_STORAGE_CE = 1 << 1;
// For 9.0 Ext4CryptPie.cpp
static constexpr int STORAGE_FLAG_DE = 1 << 0;
static constexpr int STORAGE_FLAG_CE = 1 << 1;
int Get_Password_Type(const userid_t user_id, std::string& filename);
bool Decrypt_DE();
bool Decrypt_User(const userid_t user_id, const std::string& Password);
__END_DECLS
-450
View File
@@ -1,450 +0,0 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "Ext4Crypt.h"
#include "Decrypt.h"
#ifdef USE_KEYSTORAGE_3
#include "KeyStorage3.h"
#else
#include "KeyStorage.h"
#endif
#include "Utils.h"
#include <algorithm>
#include <iomanip>
#include <map>
#include <set>
#include <sstream>
#include <string>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <openssl/sha.h>
#include <selinux/android.h>
#include <stdio.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <iostream>
#include <private/android_filesystem_config.h>
#ifdef HAVE_SYNTH_PWD_SUPPORT
#include <ext4_utils/ext4_crypt.h>
#else
#include "ext4_crypt.h"
#endif
#ifndef HAVE_LIBKEYUTILS
#include "key_control.h"
#else
#include <keyutils.h>
#endif
#include <hardware/gatekeeper.h>
#include "HashPassword.h"
#define EMULATED_USES_SELINUX 0
#define MANAGE_MISC_DIRS 0
#include <cutils/fs.h>
#include <cutils/properties.h>
#include <android-base/file.h>
//#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#define LOG(x) std::cout
#define PLOG(x) std::cout
#define DATA_MNT_POINT "/data"
using android::base::StringPrintf;
using android::vold::kEmptyAuthentication;
// NOTE: keep in sync with StorageManager
//static constexpr int FLAG_STORAGE_DE = 1 << 0; // moved to Decrypt.h
//static constexpr int FLAG_STORAGE_CE = 1 << 1;
// Store main DE raw ref / policy
std::string de_raw_ref;
// Map user ids to key references
std::map<userid_t, std::string> s_de_key_raw_refs;
std::map<userid_t, std::string> s_ce_key_raw_refs;
namespace {
const std::string device_key_dir = std::string() + DATA_MNT_POINT + e4crypt_unencrypted_folder;
const std::string device_key_path = device_key_dir + "/key";
const std::string device_key_temp = device_key_dir + "/temp";
const std::string user_key_dir = std::string() + DATA_MNT_POINT + "/misc/vold/user_keys";
const std::string user_key_temp = user_key_dir + "/temp";
bool s_global_de_initialized = false;
// Some users are ephemeral, don't try to wipe their keys from disk
std::set<userid_t> s_ephemeral_users;
// TODO abolish this map. Keys should not be long-lived in user memory, only kernel memory.
// See b/26948053
std::map<userid_t, std::string> s_ce_keys;
// ext4enc:TODO get this const from somewhere good
const int EXT4_KEY_DESCRIPTOR_SIZE = 8;
// ext4enc:TODO Include structure from somewhere sensible
// MUST be in sync with ext4_crypto.c in kernel
constexpr int EXT4_ENCRYPTION_MODE_AES_256_XTS = 1;
constexpr int EXT4_AES_256_XTS_KEY_SIZE = 64;
constexpr int EXT4_MAX_KEY_SIZE = 64;
struct ext4_encryption_key {
uint32_t mode;
char raw[EXT4_MAX_KEY_SIZE];
uint32_t size;
};
}
static bool e4crypt_is_emulated() {
return false; //property_get_bool("persist.sys.emulate_fbe", false);
}
static const char* escape_null(const char* value) {
return (value == nullptr) ? "null" : value;
}
// Get raw keyref - used to make keyname and to pass to ioctl
static std::string generate_key_ref(const char* key, int length) {
SHA512_CTX c;
SHA512_Init(&c);
SHA512_Update(&c, key, length);
unsigned char key_ref1[SHA512_DIGEST_LENGTH];
SHA512_Final(key_ref1, &c);
SHA512_Init(&c);
SHA512_Update(&c, key_ref1, SHA512_DIGEST_LENGTH);
unsigned char key_ref2[SHA512_DIGEST_LENGTH];
SHA512_Final(key_ref2, &c);
static_assert(EXT4_KEY_DESCRIPTOR_SIZE <= SHA512_DIGEST_LENGTH,
"Hash too short for descriptor");
return std::string((char*)key_ref2, EXT4_KEY_DESCRIPTOR_SIZE);
}
static bool fill_key(const std::string& key, ext4_encryption_key* ext4_key) {
if (key.size() != EXT4_AES_256_XTS_KEY_SIZE) {
LOG(ERROR) << "Wrong size key " << key.size();
return false;
}
static_assert(EXT4_AES_256_XTS_KEY_SIZE <= sizeof(ext4_key->raw), "Key too long!");
ext4_key->mode = EXT4_ENCRYPTION_MODE_AES_256_XTS;
ext4_key->size = key.size();
memset(ext4_key->raw, 0, sizeof(ext4_key->raw));
memcpy(ext4_key->raw, key.data(), key.size());
return true;
}
static std::string keyname(const std::string& raw_ref) {
std::ostringstream o;
o << "ext4:";
for (auto i : raw_ref) {
o << std::hex << std::setw(2) << std::setfill('0') << (int)i;
}
LOG(INFO) << "keyname is " << o.str() << "\n";
return o.str();
}
// Get the keyring we store all keys in
static bool e4crypt_keyring(key_serial_t* device_keyring) {
*device_keyring = keyctl_search(KEY_SPEC_SESSION_KEYRING, "keyring", "e4crypt", 0);
if (*device_keyring == -1) {
PLOG(ERROR) << "Unable to find device keyring\n";
return false;
}
return true;
}
// Install password into global keyring
// Return raw key reference for use in policy
static bool install_key(const std::string& key, std::string* raw_ref) {
ext4_encryption_key ext4_key;
if (!fill_key(key, &ext4_key)) return false;
*raw_ref = generate_key_ref(ext4_key.raw, ext4_key.size);
auto ref = keyname(*raw_ref);
key_serial_t device_keyring;
if (!e4crypt_keyring(&device_keyring)) return false;
key_serial_t key_id =
add_key("logon", ref.c_str(), (void*)&ext4_key, sizeof(ext4_key), device_keyring);
if (key_id == -1) {
PLOG(ERROR) << "Failed to insert key into keyring " << device_keyring << "\n";
return false;
}
LOG(DEBUG) << "Added key " << key_id << " (" << ref << ") to keyring " << device_keyring
<< " in process " << getpid() << "\n";
return true;
}
static std::string get_de_key_path(userid_t user_id) {
LOG(INFO) << "get_de_key_path " << user_id << " " << StringPrintf("%s/de/%d", user_key_dir.c_str(), user_id) << "\n";
return StringPrintf("%s/de/%d", user_key_dir.c_str(), user_id);
}
static std::string get_ce_key_directory_path(userid_t user_id) {
LOG(INFO) << "get_ce_key_directory_path " << user_id << ": " << StringPrintf("%s/ce/%d", user_key_dir.c_str(), user_id) << "\n";
return StringPrintf("%s/ce/%d", user_key_dir.c_str(), user_id);
}
// Returns the keys newest first
static std::vector<std::string> get_ce_key_paths(const std::string& directory_path) {
auto dirp = std::unique_ptr<DIR, int (*)(DIR*)>(opendir(directory_path.c_str()), closedir);
if (!dirp) {
PLOG(ERROR) << "Unable to open ce key directory: " + directory_path;
return std::vector<std::string>();
}
std::vector<std::string> result;
for (;;) {
errno = 0;
auto const entry = readdir(dirp.get());
if (!entry) {
if (errno) {
PLOG(ERROR) << "Unable to read ce key directory: " + directory_path;
return std::vector<std::string>();
}
break;
}
if (entry->d_type != DT_DIR || entry->d_name[0] != 'c') {
LOG(DEBUG) << "Skipping non-key " << entry->d_name;
continue;
}
result.emplace_back(directory_path + "/" + entry->d_name);
LOG(INFO) << "get_ce_key_paths adding: " << directory_path + "/" + entry->d_name << "\n";
}
std::sort(result.begin(), result.end());
std::reverse(result.begin(), result.end());
return result;
}
static std::string get_ce_key_current_path(const std::string& directory_path) {
LOG(INFO) << "get_ce_key_current_path: " << directory_path + "/current\n";
return directory_path + "/current";
}
// Discard all keys but the named one; rename it to canonical name.
// No point in acting on errors in this; ignore them.
static void fixate_user_ce_key(const std::string& directory_path, const std::string &to_fix,
const std::vector<std::string>& paths) {
for (auto const other_path: paths) {
if (other_path != to_fix) {
android::vold::destroyKey(other_path);
}
}
auto const current_path = get_ce_key_current_path(directory_path);
if (to_fix != current_path) {
LOG(DEBUG) << "Renaming " << to_fix << " to " << current_path;
if (rename(to_fix.c_str(), current_path.c_str()) != 0) {
PLOG(WARNING) << "Unable to rename " << to_fix << " to " << current_path;
}
}
}
static bool read_and_fixate_user_ce_key(userid_t user_id,
const android::vold::KeyAuthentication& auth,
std::string *ce_key) {
auto const directory_path = get_ce_key_directory_path(user_id);
auto const paths = get_ce_key_paths(directory_path);
for (auto const ce_key_path: paths) {
LOG(DEBUG) << "Trying user CE key " << ce_key_path;
if (android::vold::retrieveKey(ce_key_path, auth, ce_key)) {
LOG(DEBUG) << "Successfully retrieved key";
fixate_user_ce_key(directory_path, ce_key_path, paths);
return true;
}
}
LOG(ERROR) << "Failed to find working ce key for user " << user_id;
return false;
}
static bool read_and_install_user_ce_key(userid_t user_id,
const android::vold::KeyAuthentication& auth) {
if (s_ce_key_raw_refs.count(user_id) != 0) return true;
std::string ce_key;
if (!read_and_fixate_user_ce_key(user_id, auth, &ce_key)) return false;
std::string ce_raw_ref;
if (!install_key(ce_key, &ce_raw_ref)) return false;
s_ce_keys[user_id] = ce_key;
s_ce_key_raw_refs[user_id] = ce_raw_ref;
LOG(DEBUG) << "Installed ce key for user " << user_id;
return true;
}
static bool prepare_dir(const std::string& dir, mode_t mode, uid_t uid, gid_t gid) {
LOG(DEBUG) << "Preparing: " << dir << "\n";
return true;
return access(dir.c_str(), F_OK) == 0; // we don't want recovery creating directories or changing permissions at this point, so we will just return true if the path already exists
if (fs_prepare_dir(dir.c_str(), mode, uid, gid) != 0) {
PLOG(ERROR) << "Failed to prepare " << dir;
return false;
}
return true;
}
static bool path_exists(const std::string& path) {
return access(path.c_str(), F_OK) == 0;
}
bool lookup_key_ref(const std::map<userid_t, std::string>& key_map, userid_t user_id,
std::string* raw_ref) {
auto refi = key_map.find(user_id);
if (refi == key_map.end()) {
LOG(ERROR) << "Cannot find key for " << user_id;
return false;
}
*raw_ref = refi->second;
return true;
}
static bool is_numeric(const char* name) {
for (const char* p = name; *p != '\0'; p++) {
if (!isdigit(*p)) return false;
}
return true;
}
static bool load_all_de_keys() {
auto de_dir = user_key_dir + "/de";
auto dirp = std::unique_ptr<DIR, int (*)(DIR*)>(opendir(de_dir.c_str()), closedir);
if (!dirp) {
PLOG(ERROR) << "Unable to read de key directory";
return false;
}
for (;;) {
errno = 0;
auto entry = readdir(dirp.get());
if (!entry) {
if (errno) {
PLOG(ERROR) << "Unable to read de key directory";
return false;
}
break;
}
if (entry->d_type != DT_DIR || !is_numeric(entry->d_name)) {
LOG(DEBUG) << "Skipping non-de-key " << entry->d_name;
continue;
}
userid_t user_id = atoi(entry->d_name);
if (s_de_key_raw_refs.count(user_id) == 0) {
auto key_path = de_dir + "/" + entry->d_name;
std::string key;
if (!android::vold::retrieveKey(key_path, kEmptyAuthentication, &key)) return false;
std::string raw_ref;
if (!install_key(key, &raw_ref)) return false;
s_de_key_raw_refs[user_id] = raw_ref;
LOG(DEBUG) << "Installed de key for user " << user_id;
std::string user_prop = "twrp.user." + std::to_string(user_id) + ".decrypt";
property_set(user_prop.c_str(), "0");
}
}
// ext4enc:TODO: go through all DE directories, ensure that all user dirs have the
// correct policy set on them, and that no rogue ones exist.
return true;
}
bool e4crypt_initialize_global_de() {
if (s_global_de_initialized) {
LOG(INFO) << "Already initialized\n";
return true;
}
std::string device_key;
if (path_exists(device_key_path)) {
if (!android::vold::retrieveKey(device_key_path,
kEmptyAuthentication, &device_key)) return false;
} else {
LOG(INFO) << "NOT Creating new key\n";
return false;
}
std::string device_key_ref;
if (!install_key(device_key, &device_key_ref)) {
LOG(ERROR) << "Failed to install device key\n";
return false;
}
s_global_de_initialized = true;
de_raw_ref = device_key_ref;
return true;
}
bool e4crypt_init_user0() {
if (e4crypt_is_native()) {
if (!prepare_dir(user_key_dir, 0700, AID_ROOT, AID_ROOT)) return false;
if (!prepare_dir(user_key_dir + "/ce", 0700, AID_ROOT, AID_ROOT)) return false;
if (!prepare_dir(user_key_dir + "/de", 0700, AID_ROOT, AID_ROOT)) return false;
if (!path_exists(get_de_key_path(0))) {
//if (!create_and_install_user_keys(0, false)) return false;
printf("de key path not found\n");
return false;
}
// TODO: switch to loading only DE_0 here once framework makes
// explicit calls to install DE keys for secondary users
if (!load_all_de_keys()) return false;
}
// If this is a non-FBE device that recently left an emulated mode,
// restore user data directories to known-good state.
if (!e4crypt_is_native() && !e4crypt_is_emulated()) {
e4crypt_unlock_user_key(0, 0, "!", "!");
}
return true;
}
static bool parse_hex(const char* hex, std::string* result) {
if (strcmp("!", hex) == 0) {
*result = "";
return true;
}
if (android::vold::HexToStr(hex, *result) != 0) {
LOG(ERROR) << "Invalid FBE hex string"; // Don't log the string for security reasons
return false;
}
return true;
}
// TODO: rename to 'install' for consistency, and take flags to know which keys to install
bool e4crypt_unlock_user_key(userid_t user_id, int serial __unused, const char* token_hex,
const char* secret_hex) {
if (e4crypt_is_native()) {
if (s_ce_key_raw_refs.count(user_id) != 0) {
LOG(WARNING) << "Tried to unlock already-unlocked key for user " << user_id;
return true;
}
std::string token, secret;
if (!parse_hex(token_hex, &token)) return false;
if (!parse_hex(secret_hex, &secret)) return false;
android::vold::KeyAuthentication auth(token, secret);
if (!read_and_install_user_ce_key(user_id, auth)) {
LOG(ERROR) << "Couldn't read key for " << user_id;
return false;
}
} else {
printf("Emulation mode not supported in TWRP\n");
}
return true;
}
-47
View File
@@ -1,47 +0,0 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdbool.h>
#include <sys/cdefs.h>
#include <cutils/multiuser.h>
#include <map>
#include <string>
__BEGIN_DECLS
// General functions
bool e4crypt_is_native();
bool e4crypt_initialize_global_de();
bool e4crypt_init_user0();
//bool e4crypt_vold_create_user_key(userid_t user_id, int serial, bool ephemeral);
//bool e4crypt_destroy_user_key(userid_t user_id);
//bool e4crypt_add_user_key_auth(userid_t user_id, int serial, const char* token,
// const char* secret);
//bool e4crypt_fixate_newest_user_key_auth(userid_t user_id);
bool e4crypt_unlock_user_key(userid_t user_id, int serial, const char* token, const char* secret);
//bool e4crypt_lock_user_key(userid_t user_id);
//bool e4crypt_prepare_user_storage(const char* volume_uuid, userid_t user_id, int serial, int flags);
//bool e4crypt_destroy_user_storage(const char* volume_uuid, userid_t user_id, int flags);
bool lookup_key_ref(const std::map<userid_t, std::string>& key_map, userid_t user_id,
std::string* raw_ref);
__END_DECLS
-477
View File
@@ -1,477 +0,0 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "Ext4CryptPie.h"
#include "Keymaster4.h"
#include "KeyStorage4.h"
#include "KeyUtil.h"
#include "Utils.h"
#include "Decrypt.h"
//#include "VoldUtil.h"
#include <algorithm>
#include <map>
#include <set>
#include <sstream>
#include <string>
#include <vector>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <limits.h>
#include <selinux/android.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <private/android_filesystem_config.h>
//#include "android/os/IVold.h"
//#include "cryptfs.h"
#define EMULATED_USES_SELINUX 0
#define MANAGE_MISC_DIRS 0
#include <cutils/fs.h>
#include <cutils/properties.h>
#include <ext4_utils/ext4_crypt.h>
#include <keyutils.h>
#include <android-base/file.h>
//#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <iostream>
#define LOG(x) std::cout
#define PLOG(x) std::cout
#define DATA_MNT_POINT "/data"
using android::base::StringPrintf;
using android::base::WriteStringToFile;
using android::vold::kEmptyAuthentication;
using android::vold::KeyBuffer;
using android::vold::Keymaster;
using android::hardware::keymaster::V4_0::KeyFormat;
// Store main DE raw ref / policy
std::string de_raw_ref;
// Map user ids to key references
std::map<userid_t, std::string> s_de_key_raw_refs;
std::map<userid_t, std::string> s_ce_key_raw_refs;
// TODO abolish this map, per b/26948053
std::map<userid_t, KeyBuffer> s_ce_keys;
namespace {
struct PolicyKeyRef {
std::string contents_mode;
std::string filenames_mode;
std::string key_raw_ref;
};
const std::string device_key_dir = std::string() + DATA_MNT_POINT + e4crypt_unencrypted_folder;
const std::string device_key_path = device_key_dir + "/key";
const std::string device_key_temp = device_key_dir + "/temp";
const std::string user_key_dir = std::string() + DATA_MNT_POINT + "/misc/vold/user_keys";
const std::string user_key_temp = user_key_dir + "/temp";
const std::string prepare_subdirs_path = "/system/bin/vold_prepare_subdirs";
const std::string systemwide_volume_key_dir =
std::string() + DATA_MNT_POINT + "/misc/vold/volume_keys";
bool s_global_de_initialized = false;
// Some users are ephemeral, don't try to wipe their keys from disk
std::set<userid_t> s_ephemeral_users;
}
static bool e4crypt_is_emulated() {
return property_get_bool("persist.sys.emulate_fbe", false);
}
/*static const char* escape_empty(const std::string& value) {
return value.empty() ? "null" : value.c_str();
}*/
static std::string get_de_key_path(userid_t user_id) {
return StringPrintf("%s/de/%d", user_key_dir.c_str(), user_id);
}
static std::string get_ce_key_directory_path(userid_t user_id) {
return StringPrintf("%s/ce/%d", user_key_dir.c_str(), user_id);
}
// Returns the keys newest first
static std::vector<std::string> get_ce_key_paths(const std::string& directory_path) {
auto dirp = std::unique_ptr<DIR, int (*)(DIR*)>(opendir(directory_path.c_str()), closedir);
if (!dirp) {
PLOG(ERROR) << "Unable to open ce key directory: " + directory_path << std::endl;
return std::vector<std::string>();
}
std::vector<std::string> result;
for (;;) {
errno = 0;
auto const entry = readdir(dirp.get());
if (!entry) {
if (errno) {
PLOG(ERROR) << "Unable to read ce key directory: " + directory_path << std::endl;
return std::vector<std::string>();
}
break;
}
if (entry->d_type != DT_DIR || entry->d_name[0] != 'c') {
LOG(DEBUG) << "Skipping non-key " << entry->d_name << std::endl;
continue;
}
result.emplace_back(directory_path + "/" + entry->d_name);
}
std::sort(result.begin(), result.end());
std::reverse(result.begin(), result.end());
return result;
}
static std::string get_ce_key_current_path(const std::string& directory_path) {
return directory_path + "/current";
}
// Discard all keys but the named one; rename it to canonical name.
// No point in acting on errors in this; ignore them.
static void fixate_user_ce_key(const std::string& directory_path, const std::string &to_fix,
const std::vector<std::string>& paths) {
for (auto const other_path: paths) {
if (other_path != to_fix) {
android::vold::destroyKey(other_path);
}
}
auto const current_path = get_ce_key_current_path(directory_path);
if (to_fix != current_path) {
LOG(DEBUG) << "Renaming " << to_fix << " to " << current_path << std::endl;
if (rename(to_fix.c_str(), current_path.c_str()) != 0) {
PLOG(WARNING) << "Unable to rename " << to_fix << " to " << current_path << std::endl;
}
}
}
static bool read_and_fixate_user_ce_key(userid_t user_id,
const android::vold::KeyAuthentication& auth,
KeyBuffer *ce_key) {
auto const directory_path = get_ce_key_directory_path(user_id);
auto const paths = get_ce_key_paths(directory_path);
for (auto const ce_key_path: paths) {
LOG(DEBUG) << "Trying user CE key " << ce_key_path << std::endl;
if (android::vold::retrieveKey(ce_key_path, auth, ce_key)) {
LOG(DEBUG) << "Successfully retrieved key" << std::endl;
fixate_user_ce_key(directory_path, ce_key_path, paths);
return true;
}
}
LOG(ERROR) << "Failed to find working ce key for user " << user_id << std::endl;
return false;
}
static bool is_wrapped_key_supported_common(const std::string& mount_point) {
LOG(DEBUG) << "Determining wrapped-key support for " << mount_point << std::endl;
std::string wrapped_key_supported = android::base::GetProperty("fbe.data.wrappedkey", "false");
LOG(DEBUG) << "fbe.data.wrappedkey = " << wrapped_key_supported << std::endl;
if (mount_point == DATA_MNT_POINT && wrapped_key_supported == "true") {
LOG(DEBUG) << "Wrapped key supported on " << mount_point << std::endl;
return true;
} else {
return false;
}
}
bool is_wrapped_key_supported() {
return is_wrapped_key_supported_common(DATA_MNT_POINT);
}
bool is_wrapped_key_supported_external() {
return false;
}
static bool read_and_install_user_ce_key(userid_t user_id,
const android::vold::KeyAuthentication& auth) {
if (s_ce_key_raw_refs.count(user_id) != 0) return true;
KeyBuffer ce_key;
if (!read_and_fixate_user_ce_key(user_id, auth, &ce_key)) return false;
std::string ce_raw_ref;
if (is_wrapped_key_supported()) {
KeyBuffer ephemeral_wrapped_key;
if (!getEphemeralWrappedKey(KeyFormat::RAW, ce_key, &ephemeral_wrapped_key)) {
LOG(ERROR) << "Failed to export ce key";
return false;
}
ce_key = std::move(ephemeral_wrapped_key);
}
if (!android::vold::installKey(ce_key, &ce_raw_ref)) return false;
s_ce_keys[user_id] = std::move(ce_key);
s_ce_key_raw_refs[user_id] = ce_raw_ref;
LOG(DEBUG) << "Installed ce key for user " << user_id << std::endl;
return true;
}
static bool prepare_dir(const std::string& dir, mode_t mode, uid_t uid, gid_t gid) {
LOG(DEBUG) << "Preparing: " << dir << std::endl;
if (fs_prepare_dir(dir.c_str(), mode, uid, gid) != 0) {
PLOG(ERROR) << "Failed to prepare " << dir << std::endl;
return false;
}
return true;
}
// NB this assumes that there is only one thread listening for crypt commands, because
// it creates keys in a fixed location.
static bool create_and_install_user_keys(userid_t user_id, bool create_ephemeral) {
/*KeyBuffer de_key, ce_key;
if (!android::vold::randomKey(&de_key)) return false;
if (!android::vold::randomKey(&ce_key)) return false;
if (create_ephemeral) {
// If the key should be created as ephemeral, don't store it.
s_ephemeral_users.insert(user_id);
} else {
auto const directory_path = get_ce_key_directory_path(user_id);
if (!prepare_dir(directory_path, 0700, AID_ROOT, AID_ROOT)) return false;
auto const paths = get_ce_key_paths(directory_path);
std::string ce_key_path;
if (!get_ce_key_new_path(directory_path, paths, &ce_key_path)) return false;
if (!android::vold::storeKeyAtomically(ce_key_path, user_key_temp,
kEmptyAuthentication, ce_key)) return false;
fixate_user_ce_key(directory_path, ce_key_path, paths);
// Write DE key second; once this is written, all is good.
if (!android::vold::storeKeyAtomically(get_de_key_path(user_id), user_key_temp,
kEmptyAuthentication, de_key)) return false;
}
std::string de_raw_ref;
if (!android::vold::installKey(de_key, &de_raw_ref)) return false;
s_de_key_raw_refs[user_id] = de_raw_ref;
std::string ce_raw_ref;
if (!android::vold::installKey(ce_key, &ce_raw_ref)) return false;
s_ce_keys[user_id] = ce_key;
s_ce_key_raw_refs[user_id] = ce_raw_ref;
LOG(DEBUG) << "Created keys for user " << user_id;*/
LOG(DEBUG) << "TWRP not doing create_and_install_user_keys\n";
return true;
}
bool lookup_key_ref(const std::map<userid_t, std::string>& key_map, userid_t user_id,
std::string* raw_ref) {
auto refi = key_map.find(user_id);
if (refi == key_map.end()) {
LOG(ERROR) << "Cannot find key for " << user_id << std::endl;
return false;
}
*raw_ref = refi->second;
return true;
}
static void get_data_file_encryption_modes(PolicyKeyRef* key_ref) {
/*struct fstab_rec* rec = fs_mgr_get_entry_for_mount_point(fstab_default, DATA_MNT_POINT);
char const* contents_mode = strdup("ice");
char const* filenames_mode = strdup("aes-256-heh");
fs_mgr_get_file_encryption_modes(rec, &contents_mode, &filenames_mode);
key_ref->contents_mode = contents_mode;
key_ref->filenames_mode = filenames_mode;*/
LOG(INFO) << "contents mode '" << android::base::GetProperty("fbe.contents", "aes-256-xts") << "' filenames '" << android::base::GetProperty("fbe.filenames", "aes-256-heh") << "'\n";
key_ref->contents_mode =
android::base::GetProperty("fbe.contents", "aes-256-xts");
key_ref->filenames_mode =
android::base::GetProperty("fbe.filenames", "aes-256-heh");
}
static bool is_numeric(const char* name) {
for (const char* p = name; *p != '\0'; p++) {
if (!isdigit(*p)) return false;
}
return true;
}
static bool load_all_de_keys() {
auto de_dir = user_key_dir + "/de";
auto dirp = std::unique_ptr<DIR, int (*)(DIR*)>(opendir(de_dir.c_str()), closedir);
if (!dirp) {
PLOG(ERROR) << "Unable to read de key directory" << std::endl;
return false;
}
for (;;) {
errno = 0;
auto entry = readdir(dirp.get());
if (!entry) {
if (errno) {
PLOG(ERROR) << "Unable to read de key directory" << std::endl;
return false;
}
break;
}
if (entry->d_type != DT_DIR || !is_numeric(entry->d_name)) {
LOG(DEBUG) << "Skipping non-de-key " << entry->d_name << std::endl;
continue;
}
userid_t user_id = std::stoi(entry->d_name);
if (s_de_key_raw_refs.count(user_id) == 0) {
auto key_path = de_dir + "/" + entry->d_name;
KeyBuffer key;
if (!android::vold::retrieveKey(key_path, kEmptyAuthentication, &key)) return false;
std::string raw_ref;
if (is_wrapped_key_supported()) {
KeyBuffer ephemeral_wrapped_key;
if (!getEphemeralWrappedKey(KeyFormat::RAW, key, &ephemeral_wrapped_key)) {
LOG(ERROR) << "Failed to export de_key in create_and_install_user_keys";
return false;
}
key = std::move(ephemeral_wrapped_key);
}
if (!android::vold::installKey(key, &raw_ref)) return false;
s_de_key_raw_refs[user_id] = raw_ref;
LOG(DEBUG) << "Installed de key for user " << user_id << std::endl;
std::string user_prop = "twrp.user." + std::to_string(user_id) + ".decrypt";
property_set(user_prop.c_str(), "0");
}
}
// ext4enc:TODO: go through all DE directories, ensure that all user dirs have the
// correct policy set on them, and that no rogue ones exist.
return true;
}
bool e4crypt_initialize_global_de() {
LOG(INFO) << "e4crypt_initialize_global_de" << std::endl;
bool wrapped_key_supported = false;
if (s_global_de_initialized) {
LOG(INFO) << "Already initialized" << std::endl;
return true;
}
PolicyKeyRef device_ref;
wrapped_key_supported = is_wrapped_key_supported();
LOG(INFO) << "calling retrieveAndInstallKey\n";
if (!android::vold::retrieveAndInstallKey(true, kEmptyAuthentication, device_key_path,
device_key_temp, &device_ref.key_raw_ref, wrapped_key_supported))
return false;
get_data_file_encryption_modes(&device_ref);
std::string modestring = device_ref.contents_mode + ":" + device_ref.filenames_mode;
std::string mode_filename = std::string("/data") + e4crypt_key_mode;
if (!android::base::WriteStringToFile(modestring, mode_filename)) {
PLOG(ERROR) << "Cannot save type" << std::endl;
return false;
}
std::string ref_filename = std::string("/data") + e4crypt_key_ref;
if (!android::base::WriteStringToFile(device_ref.key_raw_ref, ref_filename)) {
PLOG(ERROR) << "Cannot save key reference to:" << ref_filename << std::endl;
return false;
}
LOG(INFO) << "Wrote system DE key reference to:" << ref_filename << std::endl;
s_global_de_initialized = true;
de_raw_ref = device_ref.key_raw_ref;
return true;
}
bool e4crypt_init_user0() {
LOG(DEBUG) << "e4crypt_init_user0\n";
if (e4crypt_is_native()) {
if (!prepare_dir(user_key_dir, 0700, AID_ROOT, AID_ROOT)) return false;
if (!prepare_dir(user_key_dir + "/ce", 0700, AID_ROOT, AID_ROOT)) return false;
if (!prepare_dir(user_key_dir + "/de", 0700, AID_ROOT, AID_ROOT)) return false;
if (!android::vold::pathExists(get_de_key_path(0))) {
if (!create_and_install_user_keys(0, false)) return false;
}
// TODO: switch to loading only DE_0 here once framework makes
// explicit calls to install DE keys for secondary users
if (!load_all_de_keys()) return false;
}
// If this is a non-FBE device that recently left an emulated mode,
// restore user data directories to known-good state.
if (!e4crypt_is_native() && !e4crypt_is_emulated()) {
e4crypt_unlock_user_key(0, 0, "!", "!");
}
return true;
}
static bool emulated_unlock(const std::string& path, mode_t mode) {
if (chmod(path.c_str(), mode) != 0) {
PLOG(ERROR) << "Failed to chmod " << path << std::endl;
// FIXME temporary workaround for b/26713622
if (e4crypt_is_emulated()) return false;
}
#if EMULATED_USES_SELINUX
if (selinux_android_restorecon(path.c_str(), SELINUX_ANDROID_RESTORECON_FORCE) != 0) {
PLOG(WARNING) << "Failed to restorecon " << path << std::endl;
// FIXME temporary workaround for b/26713622
if (e4crypt_is_emulated()) return false;
}
#endif
return true;
}
static bool parse_hex(const std::string& hex, std::string* result) {
if (hex == "!") {
*result = "";
return true;
}
if (android::vold::HexToStr(hex, *result) != 0) {
LOG(ERROR) << "Invalid FBE hex string" << std::endl; // Don't log the string for security reasons
return false;
}
return true;
}
// TODO: rename to 'install' for consistency, and take flags to know which keys to install
bool e4crypt_unlock_user_key(userid_t user_id, int serial, const std::string& token_hex,
const std::string& secret_hex) {
LOG(DEBUG) << "e4crypt_unlock_user_key " << user_id << " serial=" << serial
<< " token_present=" << (token_hex != "!") << std::endl;
if (e4crypt_is_native()) {
if (s_ce_key_raw_refs.count(user_id) != 0) {
LOG(WARNING) << "Tried to unlock already-unlocked key for user " << user_id << std::endl;
return true;
}
std::string token, secret;
if (!parse_hex(token_hex, &token)) return false;
if (!parse_hex(secret_hex, &secret)) return false;
android::vold::KeyAuthentication auth(token, secret);
if (!read_and_install_user_ce_key(user_id, auth)) {
LOG(ERROR) << "Couldn't read key for " << user_id << std::endl;
return false;
}
} else {
// When in emulation mode, we just use chmod. However, we also
// unlock directories when not in emulation mode, to bring devices
// back into a known-good state.
if (!emulated_unlock(android::vold::BuildDataSystemCePath(user_id), 0771) ||
!emulated_unlock(android::vold::BuildDataMiscCePath(user_id), 01771) ||
!emulated_unlock(android::vold::BuildDataMediaCePath("", user_id), 0770) ||
!emulated_unlock(android::vold::BuildDataUserCePath("", user_id), 0771)) {
LOG(ERROR) << "Failed to unlock user " << user_id << std::endl;
return false;
}
}
return true;
}
-45
View File
@@ -1,45 +0,0 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <map>
#include <string>
#include <cutils/multiuser.h>
bool e4crypt_initialize_global_de();
bool e4crypt_init_user0();
/*bool e4crypt_vold_create_user_key(userid_t user_id, int serial, bool ephemeral);
bool e4crypt_destroy_user_key(userid_t user_id);
bool e4crypt_add_user_key_auth(userid_t user_id, int serial, const std::string& token,
const std::string& secret);
bool e4crypt_fixate_newest_user_key_auth(userid_t user_id);*/
bool e4crypt_unlock_user_key(userid_t user_id, int serial, const std::string& token,
const std::string& secret);
//bool e4crypt_lock_user_key(userid_t user_id);
/*bool e4crypt_prepare_user_storage(const std::string& volume_uuid, userid_t user_id, int serial,
int flags);*/
/*bool e4crypt_destroy_user_storage(const std::string& volume_uuid, userid_t user_id, int flags);
bool e4crypt_destroy_volume_keys(const std::string& volume_uuid);*/
bool is_wrapped_key_supported();
bool is_wrapped_key_supported_external();
bool lookup_key_ref(const std::map<userid_t, std::string>& key_map, userid_t user_id,
std::string* raw_ref);
-117
View File
@@ -1,117 +0,0 @@
/*
* Copyright (C) 2016 Team Win Recovery Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* This computes the "secret" used by Android as one of the parameters
* to decrypt File Based Encryption. The secret is prefixed with
* "Android FBE credential hash" padded with 0s to 128 bytes then the
* user's password is appended to the end of the 128 bytes. This string
* is then hashed with sha512 and the sha512 value is then converted to
* hex with upper-case characters.
*/
#include <stdio.h>
#include <string>
#include <stdlib.h>
#include <openssl/sha.h>
#include <openssl/hmac.h>
#include "HashPassword.h"
#define PASS_PADDING_SIZE 128
#define SHA512_HEX_SIZE SHA512_DIGEST_LENGTH * 2
#define SHA256_HEX_SIZE SHA256_DIGEST_LENGTH * 2
void* PersonalizedHashBinary(const char* prefix, const char* key, const size_t key_size) {
size_t size = PASS_PADDING_SIZE + key_size;
unsigned char* buffer = (unsigned char*)calloc(1, size);
if (!buffer) return NULL; // failed to malloc
memcpy((void*)buffer, (void*)prefix, strlen(prefix));
unsigned char* ptr = buffer + PASS_PADDING_SIZE;
memcpy((void*)ptr, key, key_size);
unsigned char hash[SHA512_DIGEST_LENGTH];
SHA512_CTX sha512;
SHA512_Init(&sha512);
SHA512_Update(&sha512, buffer, size);
SHA512_Final(hash, &sha512);
free(buffer);
void* ret = malloc(SHA512_DIGEST_LENGTH);
if (!ret) return NULL; // failed to malloc
memcpy(ret, (void*)&hash[0], SHA512_DIGEST_LENGTH);
return ret;
}
std::string PersonalizedHash(const char* prefix, const char* key, const size_t key_size) {
size_t size = PASS_PADDING_SIZE + key_size;
unsigned char* buffer = (unsigned char*)calloc(1, size);
if (!buffer) return ""; // failed to malloc
memcpy((void*)buffer, (void*)prefix, strlen(prefix));
unsigned char* ptr = buffer + PASS_PADDING_SIZE;
memcpy((void*)ptr, key, key_size);
unsigned char hash[SHA512_DIGEST_LENGTH];
SHA512_CTX sha512;
SHA512_Init(&sha512);
SHA512_Update(&sha512, buffer, size);
SHA512_Final(hash, &sha512);
int index = 0;
char hex_hash[SHA512_HEX_SIZE + 1];
for(index = 0; index < SHA512_DIGEST_LENGTH; index++)
sprintf(hex_hash + (index * 2), "%02X", hash[index]);
hex_hash[128] = 0;
std::string ret = hex_hash;
free(buffer);
return ret;
}
std::string PersonalizedHash(const char* prefix, const std::string& Password) {
return PersonalizedHash(prefix, Password.c_str(), Password.size());
}
std::string PersonalizedHashSP800(const char* label, const char* context, const char* key, const size_t key_size) {
HMAC_CTX ctx;
HMAC_CTX_init(&ctx);
HMAC_Init_ex(&ctx, key, key_size, EVP_sha256(), NULL);
unsigned int counter = 1;
endianswap(&counter);
HMAC_Update(&ctx, (const unsigned char*)&counter, 4);
HMAC_Update(&ctx, (const unsigned char*)label, strlen(label));
const unsigned char divider = 0;
HMAC_Update(&ctx, &divider, 1);
HMAC_Update(&ctx, (const unsigned char*)context, strlen(context));
unsigned int contextDisambiguation = strlen(context) * 8;
endianswap(&contextDisambiguation);
HMAC_Update(&ctx, (const unsigned char*)&contextDisambiguation, 4);
unsigned int finalValue = 256;
endianswap(&finalValue);
HMAC_Update(&ctx, (const unsigned char*)&finalValue, 4);
unsigned char output[SHA256_DIGEST_LENGTH];
unsigned int out_size = 0;
HMAC_Final(&ctx, output, &out_size);
int index = 0;
char hex_hash[SHA256_HEX_SIZE + 1];
for(index = 0; index < SHA256_DIGEST_LENGTH; index++)
sprintf(hex_hash + (index * 2), "%02x", output[index]);
hex_hash[SHA256_HEX_SIZE] = 0;
std::string ret = hex_hash;
return ret;
}
std::string HashPassword(const std::string& Password) {
const char* prefix = FBE_PERSONALIZATION;
return PersonalizedHash(prefix, Password);
}
-44
View File
@@ -1,44 +0,0 @@
/*
* Copyright (C) 2016 Team Win Recovery Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __HASH_PASSWORD_H
#define __HASH_PASSWORD_H
#include <string>
#define FBE_PERSONALIZATION "Android FBE credential hash"
#define PERSONALISATION_WEAVER_KEY "weaver-key"
#define PERSONALISATION_WEAVER_PASSWORD "weaver-pwd"
#define PERSONALISATION_APPLICATION_ID "application-id"
#define PERSONALIZATION_FBE_KEY "fbe-key"
#define PERSONALIZATION_USER_GK_AUTH "user-gk-authentication"
#define PERSONALISATION_SECDISCARDABLE "secdiscardable-transform"
#define PERSONALISATION_CONTEXT "android-synthetic-password-personalization-context"
void* PersonalizedHashBinary(const char* prefix, const char* key, const size_t key_size);
std::string PersonalizedHash(const char* prefix, const char* key, const size_t key_size);
std::string PersonalizedHash(const char* prefix, const std::string& Password);
std::string PersonalizedHashSP800(const char* label, const char* context, const char* key, const size_t key_size);
std::string HashPassword(const std::string& Password);
template <class T>
void endianswap(T *objp) {
unsigned char *memp = reinterpret_cast<unsigned char*>(objp);
std::reverse(memp, memp + sizeof(T));
}
#endif
-37
View File
@@ -1,37 +0,0 @@
/*
* 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
View File
@@ -1,63 +0,0 @@
/*
* 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
-349
View File
@@ -1,349 +0,0 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "KeyStorage.h"
#include "Keymaster.h"
#include "ScryptParameters.h"
#include "Utils.h"
#include <vector>
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <iostream>
#include <openssl/sha.h>
#include <android-base/file.h>
//#include <android-base/logging.h>
#include <cutils/properties.h>
#include <hardware/hw_auth_token.h>
#include <keymaster/authorization_set.h>
extern "C" {
#include "crypto_scrypt.h"
}
#define ERROR 1
#define LOG(x) std::cout
#define PLOG(x) std::cout
namespace android {
namespace vold {
const KeyAuthentication kEmptyAuthentication{"", ""};
static constexpr size_t AES_KEY_BYTES = 32;
static constexpr size_t GCM_NONCE_BYTES = 12;
static constexpr size_t GCM_MAC_BYTES = 16;
static constexpr size_t SALT_BYTES = 1 << 4;
static constexpr size_t SECDISCARDABLE_BYTES = 1 << 14;
static constexpr size_t STRETCHED_BYTES = 1 << 6;
static constexpr uint32_t AUTH_TIMEOUT = 30; // Seconds
static const char* kCurrentVersion = "1";
static const char* kRmPath = "/system/bin/rm";
static const char* kSecdiscardPath = "/system/bin/secdiscard";
static const char* kStretch_none = "none";
static const char* kStretch_nopassword = "nopassword";
static const std::string kStretchPrefix_scrypt = "scrypt ";
static const char* kFn_encrypted_key = "encrypted_key";
static const char* kFn_keymaster_key_blob = "keymaster_key_blob";
static const char* kFn_salt = "salt";
static const char* kFn_secdiscardable = "secdiscardable";
static const char* kFn_stretching = "stretching";
static const char* kFn_version = "version";
static bool checkSize(const std::string& kind, size_t actual, size_t expected) {
if (actual != expected) {
LOG(ERROR) << "Wrong number of bytes in " << kind << ", expected " << expected << " got "
<< actual;
return false;
}
return true;
}
static std::string hashSecdiscardable(const std::string& secdiscardable) {
SHA512_CTX c;
SHA512_Init(&c);
// Personalise the hashing by introducing a fixed prefix.
// Hashing applications should use personalization except when there is a
// specific reason not to; see section 4.11 of https://www.schneier.com/skein1.3.pdf
std::string secdiscardableHashingPrefix = "Android secdiscardable SHA512";
secdiscardableHashingPrefix.resize(SHA512_CBLOCK);
SHA512_Update(&c, secdiscardableHashingPrefix.data(), secdiscardableHashingPrefix.size());
SHA512_Update(&c, secdiscardable.data(), secdiscardable.size());
std::string res(SHA512_DIGEST_LENGTH, '\0');
SHA512_Final(reinterpret_cast<uint8_t*>(&res[0]), &c);
return res;
}
/*static bool generateKeymasterKey(Keymaster& keymaster, const KeyAuthentication& auth,
const std::string& appId, std::string* key) {
auto paramBuilder = keymaster::AuthorizationSetBuilder()
.AesEncryptionKey(AES_KEY_BYTES * 8)
.Authorization(keymaster::TAG_BLOCK_MODE, KM_MODE_GCM)
.Authorization(keymaster::TAG_MIN_MAC_LENGTH, GCM_MAC_BYTES * 8)
.Authorization(keymaster::TAG_PADDING, KM_PAD_NONE);
addStringParam(&paramBuilder, keymaster::TAG_APPLICATION_ID, appId);
if (auth.token.empty()) {
LOG(DEBUG) << "Creating key that doesn't need auth token";
paramBuilder.Authorization(keymaster::TAG_NO_AUTH_REQUIRED);
} else {
LOG(DEBUG) << "Auth token required for key";
if (auth.token.size() != sizeof(hw_auth_token_t)) {
LOG(ERROR) << "Auth token should be " << sizeof(hw_auth_token_t) << " bytes, was "
<< auth.token.size() << " bytes";
return false;
}
const hw_auth_token_t* at = reinterpret_cast<const hw_auth_token_t*>(auth.token.data());
paramBuilder.Authorization(keymaster::TAG_USER_SECURE_ID, at->user_id);
paramBuilder.Authorization(keymaster::TAG_USER_AUTH_TYPE, HW_AUTH_PASSWORD);
paramBuilder.Authorization(keymaster::TAG_AUTH_TIMEOUT, AUTH_TIMEOUT);
}
return keymaster.generateKey(paramBuilder.build(), key);
}*/
static keymaster::AuthorizationSetBuilder beginParams(const KeyAuthentication& auth,
const std::string& appId) {
auto paramBuilder = keymaster::AuthorizationSetBuilder()
.Authorization(keymaster::TAG_BLOCK_MODE, KM_MODE_GCM)
.Authorization(keymaster::TAG_MAC_LENGTH, GCM_MAC_BYTES * 8)
.Authorization(keymaster::TAG_PADDING, KM_PAD_NONE);
addStringParam(&paramBuilder, keymaster::TAG_APPLICATION_ID, appId);
if (!auth.token.empty()) {
LOG(DEBUG) << "Supplying auth token to Keymaster";
addStringParam(&paramBuilder, keymaster::TAG_AUTH_TOKEN, auth.token);
}
return paramBuilder;
}
/*static bool encryptWithKeymasterKey(Keymaster& keymaster, const std::string& key,
const KeyAuthentication& auth, const std::string& appId,
const std::string& message, std::string* ciphertext) {
auto params = beginParams(auth, appId).build();
keymaster::AuthorizationSet outParams;
auto opHandle = keymaster.begin(KM_PURPOSE_ENCRYPT, key, params, &outParams);
if (!opHandle) return false;
keymaster_blob_t nonceBlob;
if (!outParams.GetTagValue(keymaster::TAG_NONCE, &nonceBlob)) {
LOG(ERROR) << "GCM encryption but no nonce generated";
return false;
}
// nonceBlob here is just a pointer into existing data, must not be freed
std::string nonce(reinterpret_cast<const char*>(nonceBlob.data), nonceBlob.data_length);
if (!checkSize("nonce", nonce.size(), GCM_NONCE_BYTES)) return false;
std::string body;
if (!opHandle.updateCompletely(message, &body)) return false;
std::string mac;
if (!opHandle.finishWithOutput(&mac)) return false;
if (!checkSize("mac", mac.size(), GCM_MAC_BYTES)) return false;
*ciphertext = nonce + body + mac;
return true;
}*/
static bool decryptWithKeymasterKey(Keymaster& keymaster, const std::string& key,
const KeyAuthentication& auth, const std::string& appId,
const std::string& ciphertext, std::string* message) {
auto nonce = ciphertext.substr(0, GCM_NONCE_BYTES);
auto bodyAndMac = ciphertext.substr(GCM_NONCE_BYTES);
auto params = addStringParam(beginParams(auth, appId), keymaster::TAG_NONCE, nonce).build();
auto opHandle = keymaster.begin(KM_PURPOSE_DECRYPT, key, params);
if (!opHandle) return false;
if (!opHandle.updateCompletely(bodyAndMac, message)) return false;
if (!opHandle.finish()) return false;
return true;
}
static bool readFileToString(const std::string& filename, std::string* result) {
if (!android::base::ReadFileToString(filename, result)) {
PLOG(ERROR) << "Failed to read from " << filename;
return false;
}
return true;
}
/*static bool writeStringToFile(const std::string& payload, const std::string& filename) {
if (!android::base::WriteStringToFile(payload, filename)) {
PLOG(ERROR) << "Failed to write to " << filename;
return false;
}
return true;
}*/
static std::string getStretching() {
char paramstr[PROPERTY_VALUE_MAX];
property_get(SCRYPT_PROP, paramstr, SCRYPT_DEFAULTS);
return std::string() + kStretchPrefix_scrypt + paramstr;
}
static bool stretchingNeedsSalt(const std::string& stretching) {
return stretching != kStretch_nopassword && stretching != kStretch_none;
}
static bool stretchSecret(const std::string& stretching, const std::string& secret,
const std::string& salt, std::string* stretched) {
if (stretching == kStretch_nopassword) {
if (!secret.empty()) {
LOG(WARNING) << "Password present but stretching is nopassword";
// Continue anyway
}
stretched->clear();
} else if (stretching == kStretch_none) {
*stretched = secret;
} else if (std::equal(kStretchPrefix_scrypt.begin(), kStretchPrefix_scrypt.end(),
stretching.begin())) {
int Nf, rf, pf;
if (!parse_scrypt_parameters(stretching.substr(kStretchPrefix_scrypt.size()).c_str(), &Nf,
&rf, &pf)) {
LOG(ERROR) << "Unable to parse scrypt params in stretching: " << stretching;
return false;
}
stretched->assign(STRETCHED_BYTES, '\0');
if (crypto_scrypt(reinterpret_cast<const uint8_t*>(secret.data()), secret.size(),
reinterpret_cast<const uint8_t*>(salt.data()), salt.size(),
1 << Nf, 1 << rf, 1 << pf,
reinterpret_cast<uint8_t*>(&(*stretched)[0]), stretched->size()) != 0) {
LOG(ERROR) << "scrypt failed with params: " << stretching;
return false;
}
} else {
LOG(ERROR) << "Unknown stretching type: " << stretching;
return false;
}
return true;
}
static bool generateAppId(const KeyAuthentication& auth, const std::string& stretching,
const std::string& salt, const std::string& secdiscardable,
std::string* appId) {
std::string stretched;
if (!stretchSecret(stretching, auth.secret, salt, &stretched)) return false;
*appId = hashSecdiscardable(secdiscardable) + stretched;
return true;
}
/*bool storeKey(const std::string& dir, const KeyAuthentication& auth, const std::string& key) {
if (TEMP_FAILURE_RETRY(mkdir(dir.c_str(), 0700)) == -1) {
PLOG(ERROR) << "key mkdir " << dir;
return false;
}
if (!writeStringToFile(kCurrentVersion, dir + "/" + kFn_version)) return false;
std::string secdiscardable;
if (ReadRandomBytes(SECDISCARDABLE_BYTES, secdiscardable) != OK) {
// TODO status_t plays badly with PLOG, fix it.
LOG(ERROR) << "Random read failed";
return false;
}
if (!writeStringToFile(secdiscardable, dir + "/" + kFn_secdiscardable)) return false;
std::string stretching = auth.secret.empty() ? kStretch_nopassword : getStretching();
if (!writeStringToFile(stretching, dir + "/" + kFn_stretching)) return false;
std::string salt;
if (stretchingNeedsSalt(stretching)) {
if (ReadRandomBytes(SALT_BYTES, salt) != OK) {
LOG(ERROR) << "Random read failed";
return false;
}
if (!writeStringToFile(salt, dir + "/" + kFn_salt)) return false;
}
std::string appId;
if (!generateAppId(auth, stretching, salt, secdiscardable, &appId)) return false;
Keymaster keymaster;
if (!keymaster) return false;
std::string kmKey;
if (!generateKeymasterKey(keymaster, auth, appId, &kmKey)) return false;
if (!writeStringToFile(kmKey, dir + "/" + kFn_keymaster_key_blob)) return false;
std::string encryptedKey;
if (!encryptWithKeymasterKey(keymaster, kmKey, auth, appId, key, &encryptedKey)) return false;
if (!writeStringToFile(encryptedKey, dir + "/" + kFn_encrypted_key)) return false;
return true;
}*/
bool retrieveKey(const std::string& dir, const KeyAuthentication& auth, std::string* key) {
std::string version;
if (!readFileToString(dir + "/" + kFn_version, &version)) return false;
if (version != kCurrentVersion) {
LOG(ERROR) << "Version mismatch, expected " << kCurrentVersion << " got " << version;
return false;
}
std::string secdiscardable;
if (!readFileToString(dir + "/" + kFn_secdiscardable, &secdiscardable)) return false;
std::string stretching;
if (!readFileToString(dir + "/" + kFn_stretching, &stretching)) return false;
std::string salt;
if (stretchingNeedsSalt(stretching)) {
if (!readFileToString(dir + "/" + kFn_salt, &salt)) return false;
}
std::string appId;
if (!generateAppId(auth, stretching, salt, secdiscardable, &appId)) return false;
std::string kmKey;
if (!readFileToString(dir + "/" + kFn_keymaster_key_blob, &kmKey)) return false;
std::string encryptedMessage;
if (!readFileToString(dir + "/" + kFn_encrypted_key, &encryptedMessage)) return false;
Keymaster keymaster;
if (!keymaster) return false;
return decryptWithKeymasterKey(keymaster, kmKey, auth, appId, encryptedMessage, key);
}
static bool deleteKey(const std::string& dir) {
std::string kmKey;
if (!readFileToString(dir + "/" + kFn_keymaster_key_blob, &kmKey)) return false;
Keymaster keymaster;
if (!keymaster) return false;
if (!keymaster.deleteKey(kmKey)) return false;
return true;
}
static bool secdiscardSecdiscardable(const std::string& dir) {
if (ForkExecvp(
std::vector<std::string>{kSecdiscardPath, "--", dir + "/" + kFn_secdiscardable}) != 0) {
LOG(ERROR) << "secdiscard failed";
return false;
}
return true;
}
static bool recursiveDeleteKey(const std::string& dir) {
if (ForkExecvp(std::vector<std::string>{kRmPath, "-rf", dir}) != 0) {
LOG(ERROR) << "recursive delete failed";
return false;
}
return true;
}
bool destroyKey(const std::string& dir) {
bool success = true;
// Try each thing, even if previous things failed.
success &= deleteKey(dir);
success &= secdiscardSecdiscardable(dir);
success &= recursiveDeleteKey(dir);
return success;
}
} // namespace vold
} // namespace android
-53
View File
@@ -1,53 +0,0 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef ANDROID_VOLD_KEYSTORAGE_H
#define ANDROID_VOLD_KEYSTORAGE_H
#include <string>
namespace android {
namespace vold {
// Represents the information needed to decrypt a disk encryption key.
// If "token" is nonempty, it is passed in as a required Gatekeeper auth token.
// If "secret" is nonempty, it is appended to the application-specific
// binary needed to unlock.
class KeyAuthentication {
public:
KeyAuthentication(std::string t, std::string s) : token{t}, secret{s} {};
const std::string token;
const std::string secret;
};
extern const KeyAuthentication kEmptyAuthentication;
// Create a directory at the named path, and store "key" in it,
// in such a way that it can only be retrieved via Keymaster and
// can be securely deleted.
// It's safe to move/rename the directory after creation.
//bool storeKey(const std::string& dir, const KeyAuthentication& auth, const std::string& key);
// Retrieve the key from the named directory.
bool retrieveKey(const std::string& dir, const KeyAuthentication& auth, std::string* key);
// Securely destroy the key stored in the named directory and delete the directory.
bool destroyKey(const std::string& dir);
} // namespace vold
} // namespace android
#endif
-526
View File
@@ -1,526 +0,0 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "KeyStorage3.h"
#include "Keymaster3.h"
#include "ScryptParameters.h"
#include "Utils.h"
#include <vector>
#include <errno.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/sha.h>
#include <android-base/file.h>
//#include <android-base/logging.h>
#include <cutils/properties.h>
#include <hardware/hw_auth_token.h>
#include <keystore/authorization_set.h>
#include <keystore/keystore_hidl_support.h>
extern "C" {
#include "crypto_scrypt.h"
}
#include <iostream>
#define ERROR 1
#define LOG(x) std::cout
#define PLOG(x) std::cout
namespace android {
namespace vold {
using namespace keystore;
const KeyAuthentication kEmptyAuthentication{"", ""};
static constexpr size_t AES_KEY_BYTES = 32;
static constexpr size_t GCM_NONCE_BYTES = 12;
static constexpr size_t GCM_MAC_BYTES = 16;
static constexpr size_t SALT_BYTES = 1 << 4;
static constexpr size_t SECDISCARDABLE_BYTES = 1 << 14;
static constexpr size_t STRETCHED_BYTES = 1 << 6;
static constexpr uint32_t AUTH_TIMEOUT = 30; // Seconds
static const char* kCurrentVersion = "1";
static const char* kRmPath = "/system/bin/rm";
static const char* kSecdiscardPath = "/system/bin/secdiscard";
static const char* kStretch_none = "none";
static const char* kStretch_nopassword = "nopassword";
static const std::string kStretchPrefix_scrypt = "scrypt ";
static const char* kHashPrefix_secdiscardable = "Android secdiscardable SHA512";
static const char* kHashPrefix_keygen = "Android key wrapping key generation SHA512";
static const char* kFn_encrypted_key = "encrypted_key";
static const char* kFn_keymaster_key_blob = "keymaster_key_blob";
static const char* kFn_keymaster_key_blob_upgraded = "keymaster_key_blob_upgraded";
static const char* kFn_salt = "salt";
static const char* kFn_secdiscardable = "secdiscardable";
static const char* kFn_stretching = "stretching";
static const char* kFn_version = "version";
static bool checkSize(const std::string& kind, size_t actual, size_t expected) {
if (actual != expected) {
LOG(ERROR) << "Wrong number of bytes in " << kind << ", expected " << expected << " got "
<< actual;
return false;
}
return true;
}
static std::string hashWithPrefix(char const* prefix, const std::string& tohash) {
SHA512_CTX c;
SHA512_Init(&c);
// Personalise the hashing by introducing a fixed prefix.
// Hashing applications should use personalization except when there is a
// specific reason not to; see section 4.11 of https://www.schneier.com/skein1.3.pdf
std::string hashingPrefix = prefix;
hashingPrefix.resize(SHA512_CBLOCK);
SHA512_Update(&c, hashingPrefix.data(), hashingPrefix.size());
SHA512_Update(&c, tohash.data(), tohash.size());
std::string res(SHA512_DIGEST_LENGTH, '\0');
SHA512_Final(reinterpret_cast<uint8_t*>(&res[0]), &c);
return res;
}
/*static bool generateKeymasterKey(Keymaster& keymaster, const KeyAuthentication& auth,
const std::string& appId, std::string* key) {
auto paramBuilder = AuthorizationSetBuilder()
.AesEncryptionKey(AES_KEY_BYTES * 8)
.Authorization(TAG_BLOCK_MODE, BlockMode::GCM)
.Authorization(TAG_MIN_MAC_LENGTH, GCM_MAC_BYTES * 8)
.Authorization(TAG_PADDING, PaddingMode::NONE)
.Authorization(TAG_APPLICATION_ID, blob2hidlVec(appId));
if (auth.token.empty()) {
LOG(DEBUG) << "Creating key that doesn't need auth token";
paramBuilder.Authorization(TAG_NO_AUTH_REQUIRED);
} else {
LOG(DEBUG) << "Auth token required for key";
if (auth.token.size() != sizeof(hw_auth_token_t)) {
LOG(ERROR) << "Auth token should be " << sizeof(hw_auth_token_t) << " bytes, was "
<< auth.token.size() << " bytes";
return false;
}
const hw_auth_token_t* at = reinterpret_cast<const hw_auth_token_t*>(auth.token.data());
paramBuilder.Authorization(TAG_USER_SECURE_ID, at->user_id);
paramBuilder.Authorization(TAG_USER_AUTH_TYPE, HardwareAuthenticatorType::PASSWORD);
paramBuilder.Authorization(TAG_AUTH_TIMEOUT, AUTH_TIMEOUT);
}
return keymaster.generateKey(paramBuilder, key);
}*/
static AuthorizationSet beginParams(const KeyAuthentication& auth,
const std::string& appId) {
auto paramBuilder = AuthorizationSetBuilder()
.Authorization(TAG_BLOCK_MODE, BlockMode::GCM)
.Authorization(TAG_MAC_LENGTH, GCM_MAC_BYTES * 8)
.Authorization(TAG_PADDING, PaddingMode::NONE)
.Authorization(TAG_APPLICATION_ID, blob2hidlVec(appId));
if (!auth.token.empty()) {
LOG(DEBUG) << "Supplying auth token to Keymaster";
paramBuilder.Authorization(TAG_AUTH_TOKEN, blob2hidlVec(auth.token));
}
return paramBuilder;
}
static bool readFileToString(const std::string& filename, std::string* result) {
if (!android::base::ReadFileToString(filename, result)) {
PLOG(ERROR) << "Failed to read from " << filename;
return false;
}
return true;
}
static bool writeStringToFile(const std::string& payload, const std::string& filename) {
if (!android::base::WriteStringToFile(payload, filename)) {
PLOG(ERROR) << "Failed to write to " << filename;
return false;
}
return true;
}
static KeymasterOperation begin(Keymaster& keymaster, const std::string& dir,
KeyPurpose purpose,
const AuthorizationSet &keyParams,
const AuthorizationSet &opParams,
AuthorizationSet* outParams) {
auto kmKeyPath = dir + "/" + kFn_keymaster_key_blob;
std::string kmKey;
if (!readFileToString(kmKeyPath, &kmKey)) return KeymasterOperation();
AuthorizationSet inParams(keyParams);
inParams.append(opParams.begin(), opParams.end());
for (;;) {
auto opHandle = keymaster.begin(purpose, kmKey, inParams, outParams);
if (opHandle) {
return opHandle;
}
if (opHandle.errorCode() != ErrorCode::KEY_REQUIRES_UPGRADE) return opHandle;
LOG(DEBUG) << "Upgrading key: " << dir;
std::string newKey;
if (!keymaster.upgradeKey(kmKey, keyParams, &newKey)) return KeymasterOperation();
// Upgrade the key in memory but do not replace the key in storage
/*auto newKeyPath = dir + "/" + kFn_keymaster_key_blob_upgraded;
if (!writeStringToFile(newKey, newKeyPath)) return KeymasterOperation();
if (rename(newKeyPath.c_str(), kmKeyPath.c_str()) != 0) {
PLOG(ERROR) << "Unable to move upgraded key to location: " << kmKeyPath;
return KeymasterOperation();
}
if (!keymaster.deleteKey(kmKey)) {
LOG(ERROR) << "Key deletion failed during upgrade, continuing anyway: " << dir;
}*/
kmKey = newKey;
LOG(INFO) << "Key upgraded: " << dir;
}
}
/*static bool encryptWithKeymasterKey(Keymaster& keymaster, const std::string& dir,
const AuthorizationSet &keyParams,
const std::string& message, std::string* ciphertext) {
AuthorizationSet opParams;
AuthorizationSet outParams;
auto opHandle = begin(keymaster, dir, KeyPurpose::ENCRYPT, keyParams, opParams, &outParams);
if (!opHandle) return false;
auto nonceBlob = outParams.GetTagValue(TAG_NONCE);
if (!nonceBlob.isOk()) {
LOG(ERROR) << "GCM encryption but no nonce generated";
return false;
}
// nonceBlob here is just a pointer into existing data, must not be freed
std::string nonce(reinterpret_cast<const char*>(&nonceBlob.value()[0]), nonceBlob.value().size());
if (!checkSize("nonce", nonce.size(), GCM_NONCE_BYTES)) return false;
std::string body;
if (!opHandle.updateCompletely(message, &body)) return false;
std::string mac;
if (!opHandle.finish(&mac)) return false;
if (!checkSize("mac", mac.size(), GCM_MAC_BYTES)) return false;
*ciphertext = nonce + body + mac;
return true;
}*/
static bool decryptWithKeymasterKey(Keymaster& keymaster, const std::string& dir,
const AuthorizationSet &keyParams,
const std::string& ciphertext, std::string* message) {
auto nonce = ciphertext.substr(0, GCM_NONCE_BYTES);
auto bodyAndMac = ciphertext.substr(GCM_NONCE_BYTES);
auto opParams = AuthorizationSetBuilder()
.Authorization(TAG_NONCE, blob2hidlVec(nonce));
auto opHandle = begin(keymaster, dir, KeyPurpose::DECRYPT, keyParams, opParams, nullptr);
if (!opHandle) return false;
if (!opHandle.updateCompletely(bodyAndMac, message)) return false;
if (!opHandle.finish(nullptr)) return false;
return true;
}
static std::string getStretching(const KeyAuthentication& auth) {
if (!auth.usesKeymaster()) {
return kStretch_none;
} else if (auth.secret.empty()) {
return kStretch_nopassword;
} else {
char paramstr[PROPERTY_VALUE_MAX];
property_get(SCRYPT_PROP, paramstr, SCRYPT_DEFAULTS);
return std::string() + kStretchPrefix_scrypt + paramstr;
}
}
static bool stretchingNeedsSalt(const std::string& stretching) {
return stretching != kStretch_nopassword && stretching != kStretch_none;
}
static bool stretchSecret(const std::string& stretching, const std::string& secret,
const std::string& salt, std::string* stretched) {
if (stretching == kStretch_nopassword) {
if (!secret.empty()) {
LOG(WARNING) << "Password present but stretching is nopassword";
// Continue anyway
}
stretched->clear();
} else if (stretching == kStretch_none) {
*stretched = secret;
} else if (std::equal(kStretchPrefix_scrypt.begin(), kStretchPrefix_scrypt.end(),
stretching.begin())) {
int Nf, rf, pf;
if (!parse_scrypt_parameters(stretching.substr(kStretchPrefix_scrypt.size()).c_str(), &Nf,
&rf, &pf)) {
LOG(ERROR) << "Unable to parse scrypt params in stretching: " << stretching;
return false;
}
stretched->assign(STRETCHED_BYTES, '\0');
if (crypto_scrypt(reinterpret_cast<const uint8_t*>(secret.data()), secret.size(),
reinterpret_cast<const uint8_t*>(salt.data()), salt.size(),
1 << Nf, 1 << rf, 1 << pf,
reinterpret_cast<uint8_t*>(&(*stretched)[0]), stretched->size()) != 0) {
LOG(ERROR) << "scrypt failed with params: " << stretching;
return false;
}
} else {
LOG(ERROR) << "Unknown stretching type: " << stretching;
return false;
}
return true;
}
static bool generateAppId(const KeyAuthentication& auth, const std::string& stretching,
const std::string& salt, const std::string& secdiscardable,
std::string* appId) {
std::string stretched;
if (!stretchSecret(stretching, auth.secret, salt, &stretched)) return false;
*appId = hashWithPrefix(kHashPrefix_secdiscardable, secdiscardable) + stretched;
return true;
}
static bool readRandomBytesOrLog(size_t count, std::string* out) {
auto status = ReadRandomBytes(count, *out);
if (status != OK) {
LOG(ERROR) << "Random read failed with status: " << status;
return false;
}
return true;
}
static void logOpensslError() {
LOG(ERROR) << "Openssl error: " << ERR_get_error();
}
static bool encryptWithoutKeymaster(const std::string& preKey,
const std::string& plaintext, std::string* ciphertext) {
auto key = hashWithPrefix(kHashPrefix_keygen, preKey);
key.resize(AES_KEY_BYTES);
if (!readRandomBytesOrLog(GCM_NONCE_BYTES, ciphertext)) return false;
auto ctx = std::unique_ptr<EVP_CIPHER_CTX, decltype(&::EVP_CIPHER_CTX_free)>(
EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free);
if (!ctx) {
logOpensslError();
return false;
}
if (1 != EVP_EncryptInit_ex(ctx.get(), EVP_aes_256_gcm(), NULL,
reinterpret_cast<const uint8_t*>(key.data()),
reinterpret_cast<const uint8_t*>(ciphertext->data()))) {
logOpensslError();
return false;
}
ciphertext->resize(GCM_NONCE_BYTES + plaintext.size() + GCM_MAC_BYTES);
int outlen;
if (1 != EVP_EncryptUpdate(ctx.get(),
reinterpret_cast<uint8_t*>(&(*ciphertext)[0] + GCM_NONCE_BYTES), &outlen,
reinterpret_cast<const uint8_t*>(plaintext.data()), plaintext.size())) {
logOpensslError();
return false;
}
if (outlen != static_cast<int>(plaintext.size())) {
LOG(ERROR) << "GCM ciphertext length should be " << plaintext.size() << " was " << outlen;
return false;
}
if (1 != EVP_EncryptFinal_ex(ctx.get(),
reinterpret_cast<uint8_t*>(&(*ciphertext)[0] + GCM_NONCE_BYTES + plaintext.size()), &outlen)) {
logOpensslError();
return false;
}
if (outlen != 0) {
LOG(ERROR) << "GCM EncryptFinal should be 0, was " << outlen;
return false;
}
if (1 != EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_GET_TAG, GCM_MAC_BYTES,
reinterpret_cast<uint8_t*>(&(*ciphertext)[0] + GCM_NONCE_BYTES + plaintext.size()))) {
logOpensslError();
return false;
}
return true;
}
static bool decryptWithoutKeymaster(const std::string& preKey,
const std::string& ciphertext, std::string* plaintext) {
if (ciphertext.size() < GCM_NONCE_BYTES + GCM_MAC_BYTES) {
LOG(ERROR) << "GCM ciphertext too small: " << ciphertext.size();
return false;
}
auto key = hashWithPrefix(kHashPrefix_keygen, preKey);
key.resize(AES_KEY_BYTES);
auto ctx = std::unique_ptr<EVP_CIPHER_CTX, decltype(&::EVP_CIPHER_CTX_free)>(
EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free);
if (!ctx) {
logOpensslError();
return false;
}
if (1 != EVP_DecryptInit_ex(ctx.get(), EVP_aes_256_gcm(), NULL,
reinterpret_cast<const uint8_t*>(key.data()),
reinterpret_cast<const uint8_t*>(ciphertext.data()))) {
logOpensslError();
return false;
}
plaintext->resize(ciphertext.size() - GCM_NONCE_BYTES - GCM_MAC_BYTES);
int outlen;
if (1 != EVP_DecryptUpdate(ctx.get(),
reinterpret_cast<uint8_t*>(&(*plaintext)[0]), &outlen,
reinterpret_cast<const uint8_t*>(ciphertext.data() + GCM_NONCE_BYTES), plaintext->size())) {
logOpensslError();
return false;
}
if (outlen != static_cast<int>(plaintext->size())) {
LOG(ERROR) << "GCM plaintext length should be " << plaintext->size() << " was " << outlen;
return false;
}
if (1 != EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_SET_TAG, GCM_MAC_BYTES,
const_cast<void *>(
reinterpret_cast<const void*>(ciphertext.data() + GCM_NONCE_BYTES + plaintext->size())))) {
logOpensslError();
return false;
}
if (1 != EVP_DecryptFinal_ex(ctx.get(),
reinterpret_cast<uint8_t*>(&(*plaintext)[0] + plaintext->size()), &outlen)) {
logOpensslError();
return false;
}
if (outlen != 0) {
LOG(ERROR) << "GCM EncryptFinal should be 0, was " << outlen;
return false;
}
return true;
}
/*bool storeKey(const std::string& dir, const KeyAuthentication& auth, const std::string& key) {
if (TEMP_FAILURE_RETRY(mkdir(dir.c_str(), 0700)) == -1) {
PLOG(ERROR) << "key mkdir " << dir;
return false;
}
if (!writeStringToFile(kCurrentVersion, dir + "/" + kFn_version)) return false;
std::string secdiscardable;
if (!readRandomBytesOrLog(SECDISCARDABLE_BYTES, &secdiscardable)) return false;
if (!writeStringToFile(secdiscardable, dir + "/" + kFn_secdiscardable)) return false;
std::string stretching = getStretching(auth);
if (!writeStringToFile(stretching, dir + "/" + kFn_stretching)) return false;
std::string salt;
if (stretchingNeedsSalt(stretching)) {
if (ReadRandomBytes(SALT_BYTES, salt) != OK) {
LOG(ERROR) << "Random read failed";
return false;
}
if (!writeStringToFile(salt, dir + "/" + kFn_salt)) return false;
}
std::string appId;
if (!generateAppId(auth, stretching, salt, secdiscardable, &appId)) return false;
std::string encryptedKey;
if (auth.usesKeymaster()) {
Keymaster keymaster;
if (!keymaster) return false;
std::string kmKey;
if (!generateKeymasterKey(keymaster, auth, appId, &kmKey)) return false;
if (!writeStringToFile(kmKey, dir + "/" + kFn_keymaster_key_blob)) return false;
auto keyParams = beginParams(auth, appId);
if (!encryptWithKeymasterKey(keymaster, dir, keyParams, key, &encryptedKey)) return false;
} else {
if (!encryptWithoutKeymaster(appId, key, &encryptedKey)) return false;
}
if (!writeStringToFile(encryptedKey, dir + "/" + kFn_encrypted_key)) return false;
return true;
}*/
bool retrieveKey(const std::string& dir, const KeyAuthentication& auth, std::string* key) {
std::string version;
if (!readFileToString(dir + "/" + kFn_version, &version)) return false;
if (version != kCurrentVersion) {
LOG(ERROR) << "Version mismatch, expected " << kCurrentVersion << " got " << version;
return false;
}
std::string secdiscardable;
if (!readFileToString(dir + "/" + kFn_secdiscardable, &secdiscardable)) return false;
std::string stretching;
if (!readFileToString(dir + "/" + kFn_stretching, &stretching)) return false;
std::string salt;
if (stretchingNeedsSalt(stretching)) {
if (!readFileToString(dir + "/" + kFn_salt, &salt)) return false;
}
std::string appId;
if (!generateAppId(auth, stretching, salt, secdiscardable, &appId)) return false;
std::string encryptedMessage;
if (!readFileToString(dir + "/" + kFn_encrypted_key, &encryptedMessage)) return false;
if (auth.usesKeymaster()) {
Keymaster keymaster;
if (!keymaster) return false;
auto keyParams = beginParams(auth, appId);
if (!decryptWithKeymasterKey(keymaster, dir, keyParams, encryptedMessage, key)) return false;
} else {
if (!decryptWithoutKeymaster(appId, encryptedMessage, key)) return false;
}
return true;
}
static bool deleteKey(const std::string& dir) {
std::string kmKey;
if (!readFileToString(dir + "/" + kFn_keymaster_key_blob, &kmKey)) return false;
Keymaster keymaster;
if (!keymaster) return false;
if (!keymaster.deleteKey(kmKey)) return false;
return true;
}
static bool runSecdiscard(const std::string& dir) {
if (ForkExecvp(
std::vector<std::string>{kSecdiscardPath, "--",
dir + "/" + kFn_encrypted_key,
dir + "/" + kFn_keymaster_key_blob,
dir + "/" + kFn_secdiscardable,
}) != 0) {
LOG(ERROR) << "secdiscard failed";
return false;
}
return true;
}
bool runSecdiscardSingle(const std::string& file) {
if (ForkExecvp(
std::vector<std::string>{kSecdiscardPath, "--",
file}) != 0) {
LOG(ERROR) << "secdiscard failed";
return false;
}
return true;
}
static bool recursiveDeleteKey(const std::string& dir) {
if (ForkExecvp(std::vector<std::string>{kRmPath, "-rf", dir}) != 0) {
LOG(ERROR) << "recursive delete failed";
return false;
}
return true;
}
bool destroyKey(const std::string& dir) {
bool success = true;
// Try each thing, even if previous things failed.
success &= deleteKey(dir);
success &= runSecdiscard(dir);
success &= recursiveDeleteKey(dir);
return success;
}
} // namespace vold
} // namespace android
-58
View File
@@ -1,58 +0,0 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef ANDROID_VOLD_KEYSTORAGE_H
#define ANDROID_VOLD_KEYSTORAGE_H
#include <string>
namespace android {
namespace vold {
// Represents the information needed to decrypt a disk encryption key.
// If "token" is nonempty, it is passed in as a required Gatekeeper auth token.
// If "token" and "secret" are nonempty, "secret" is appended to the application-specific
// binary needed to unlock.
// If only "secret" is nonempty, it is used to decrypt in a non-Keymaster process.
class KeyAuthentication {
public:
KeyAuthentication(std::string t, std::string s) : token{t}, secret{s} {};
bool usesKeymaster() const { return !token.empty() || secret.empty(); };
const std::string token;
const std::string secret;
};
extern const KeyAuthentication kEmptyAuthentication;
// Create a directory at the named path, and store "key" in it,
// in such a way that it can only be retrieved via Keymaster and
// can be securely deleted.
// It's safe to move/rename the directory after creation.
bool storeKey(const std::string& dir, const KeyAuthentication& auth, const std::string& key);
// Retrieve the key from the named directory.
bool retrieveKey(const std::string& dir, const KeyAuthentication& auth, std::string* key);
// Securely destroy the key stored in the named directory and delete the directory.
bool destroyKey(const std::string& dir);
bool runSecdiscardSingle(const std::string& file);
} // namespace vold
} // namespace android
#endif
-656
View File
@@ -1,656 +0,0 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "KeyStorage4.h"
#include "Keymaster4.h"
#include "ScryptParameters.h"
#include "Utils.h"
#include <vector>
#include <errno.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/sha.h>
#include <android-base/file.h>
//#include <android-base/logging.h>
#include <android-base/unique_fd.h>
#include <cutils/properties.h>
#include <hardware/hw_auth_token.h>
#include <keymasterV4_0/authorization_set.h>
#include <keymasterV4_0/keymaster_utils.h>
#include <iostream>
#define ERROR 1
#define LOG(x) std::cout
#define PLOG(x) std::cout
extern "C" {
#include "crypto_scrypt.h"
}
namespace android {
namespace vold {
const KeyAuthentication kEmptyAuthentication{"", ""};
static constexpr size_t AES_KEY_BYTES = 32;
static constexpr size_t GCM_NONCE_BYTES = 12;
static constexpr size_t GCM_MAC_BYTES = 16;
static constexpr size_t SALT_BYTES = 1 << 4;
static constexpr size_t SECDISCARDABLE_BYTES = 1 << 14;
static constexpr size_t STRETCHED_BYTES = 1 << 6;
static constexpr uint32_t AUTH_TIMEOUT = 30; // Seconds
constexpr int EXT4_AES_256_XTS_KEY_SIZE = 64;
static const char* kCurrentVersion = "1";
static const char* kRmPath = "/system/bin/rm";
static const char* kSecdiscardPath = "/system/bin/secdiscard";
static const char* kStretch_none = "none";
static const char* kStretch_nopassword = "nopassword";
static const std::string kStretchPrefix_scrypt = "scrypt ";
static const char* kHashPrefix_secdiscardable = "Android secdiscardable SHA512";
static const char* kHashPrefix_keygen = "Android key wrapping key generation SHA512";
static const char* kFn_encrypted_key = "encrypted_key";
static const char* kFn_keymaster_key_blob = "keymaster_key_blob";
static const char* kFn_keymaster_key_blob_upgraded = "keymaster_key_blob_upgraded";
static const char* kFn_salt = "salt";
static const char* kFn_secdiscardable = "secdiscardable";
static const char* kFn_stretching = "stretching";
static const char* kFn_version = "version";
static bool checkSize(const std::string& kind, size_t actual, size_t expected) {
if (actual != expected) {
LOG(ERROR) << "Wrong number of bytes in " << kind << ", expected " << expected << " got "
<< actual << std::endl;
return false;
}
return true;
}
static void hashWithPrefix(char const* prefix, const std::string& tohash, std::string* res) {
SHA512_CTX c;
SHA512_Init(&c);
// Personalise the hashing by introducing a fixed prefix.
// Hashing applications should use personalization except when there is a
// specific reason not to; see section 4.11 of https://www.schneier.com/skein1.3.pdf
std::string hashingPrefix = prefix;
hashingPrefix.resize(SHA512_CBLOCK);
SHA512_Update(&c, hashingPrefix.data(), hashingPrefix.size());
SHA512_Update(&c, tohash.data(), tohash.size());
res->assign(SHA512_DIGEST_LENGTH, '\0');
SHA512_Final(reinterpret_cast<uint8_t*>(&(*res)[0]), &c);
}
static bool generateKeymasterKey(Keymaster& keymaster, const KeyAuthentication& auth,
const std::string& appId, std::string* key) {
auto paramBuilder = km::AuthorizationSetBuilder()
.AesEncryptionKey(AES_KEY_BYTES * 8)
.GcmModeMinMacLen(GCM_MAC_BYTES * 8)
.Authorization(km::TAG_APPLICATION_ID, km::support::blob2hidlVec(appId));
if (auth.token.empty()) {
LOG(DEBUG) << "Creating key that doesn't need auth token" << std::endl;
paramBuilder.Authorization(km::TAG_NO_AUTH_REQUIRED);
} else {
LOG(DEBUG) << "Auth token required for key" << std::endl;
if (auth.token.size() != sizeof(hw_auth_token_t)) {
LOG(ERROR) << "Auth token should be " << sizeof(hw_auth_token_t) << " bytes, was "
<< auth.token.size() << " bytes" << std::endl;
return false;
}
const hw_auth_token_t* at = reinterpret_cast<const hw_auth_token_t*>(auth.token.data());
paramBuilder.Authorization(km::TAG_USER_SECURE_ID, at->user_id);
paramBuilder.Authorization(km::TAG_USER_AUTH_TYPE, km::HardwareAuthenticatorType::PASSWORD);
paramBuilder.Authorization(km::TAG_AUTH_TIMEOUT, AUTH_TIMEOUT);
}
return keymaster.generateKey(paramBuilder, key);
}
bool generateWrappedKey(userid_t user_id, KeyType key_type,
KeyBuffer* key) {
Keymaster keymaster;
if (!keymaster) return false;
*key = KeyBuffer(EXT4_AES_256_XTS_KEY_SIZE);
std::string key_temp;
auto paramBuilder = km::AuthorizationSetBuilder()
.AesEncryptionKey(AES_KEY_BYTES * 8)
.GcmModeMinMacLen(GCM_MAC_BYTES * 8)
.Authorization(km::TAG_USER_ID, user_id);
km::KeyParameter param1;
param1.tag = (km::Tag) (android::hardware::keymaster::V4_0::KM_TAG_FBE_ICE);
param1.f.boolValue = true;
paramBuilder.push_back(param1);
km::KeyParameter param2;
if ((key_type == KeyType::DE_USER) || (key_type == KeyType::DE_SYS)) {
param2.tag = (km::Tag) (android::hardware::keymaster::V4_0::KM_TAG_KEY_TYPE);
param2.f.integer = 0;
} else if (key_type == KeyType::CE_USER) {
param2.tag = (km::Tag) (android::hardware::keymaster::V4_0::KM_TAG_KEY_TYPE);
param2.f.integer = 1;
}
paramBuilder.push_back(param2);
if (!keymaster.generateKey(paramBuilder, &key_temp)) return false;
*key = KeyBuffer(key_temp.size());
memcpy(reinterpret_cast<void*>(key->data()), key_temp.c_str(), key->size());
return true;
}
bool getEphemeralWrappedKey(km::KeyFormat format, KeyBuffer& kmKey, KeyBuffer* key) {
std::string key_temp;
Keymaster keymaster;
if (!keymaster) return false;
//Export once, if upgrade needed, upgrade and export again
bool export_again = true;
while (export_again) {
export_again = false;
auto ret = keymaster.exportKey(format, kmKey, "!", "!", &key_temp);
if (ret == km::ErrorCode::OK) {
*key = KeyBuffer(key_temp.size());
memcpy(reinterpret_cast<void*>(key->data()), key_temp.c_str(), key->size());
return true;
}
if (ret != km::ErrorCode::KEY_REQUIRES_UPGRADE) return false;
LOG(DEBUG) << "Upgrading key" << std::endl;
std::string kmKeyStr(reinterpret_cast<const char*>(kmKey.data()), kmKey.size());
std::string newKey;
if (!keymaster.upgradeKey(kmKeyStr, km::AuthorizationSet(), &newKey)) return false;
memcpy(reinterpret_cast<void*>(kmKey.data()), newKey.c_str(), kmKey.size());
LOG(INFO) << "Key upgraded" << std::endl;
export_again = true;
}
//Should never come here
return false;
}
static std::pair<km::AuthorizationSet, km::HardwareAuthToken> beginParams(
const KeyAuthentication& auth, const std::string& appId) {
auto paramBuilder = km::AuthorizationSetBuilder()
.GcmModeMacLen(GCM_MAC_BYTES * 8)
.Authorization(km::TAG_APPLICATION_ID, km::support::blob2hidlVec(appId));
km::HardwareAuthToken authToken;
if (!auth.token.empty()) {
LOG(DEBUG) << "Supplying auth token to Keymaster" << std::endl;
authToken = km::support::hidlVec2AuthToken(km::support::blob2hidlVec(auth.token));
}
return {paramBuilder, authToken};
}
static bool readFileToString(const std::string& filename, std::string* result) {
if (!android::base::ReadFileToString(filename, result)) {
PLOG(ERROR) << "Failed to read from " << filename << std::endl;
return false;
}
return true;
}
static bool writeStringToFile(const std::string& payload, const std::string& filename) {
PLOG(ERROR) << __FUNCTION__ << " called for " << filename << " and being skipped\n";
return true;
android::base::unique_fd fd(TEMP_FAILURE_RETRY(
open(filename.c_str(), O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC | O_CLOEXEC, 0666)));
if (fd == -1) {
PLOG(ERROR) << "Failed to open " << filename;
return false;
}
if (!android::base::WriteStringToFd(payload, fd)) {
PLOG(ERROR) << "Failed to write to " << filename;
unlink(filename.c_str());
return false;
}
// fsync as close won't guarantee flush data
// see close(2), fsync(2) and b/68901441
if (fsync(fd) == -1) {
if (errno == EROFS || errno == EINVAL) {
PLOG(WARNING) << "Skip fsync " << filename
<< " on a file system does not support synchronization";
} else {
PLOG(ERROR) << "Failed to fsync " << filename;
unlink(filename.c_str());
return false;
}
}
return true;
}
static bool readRandomBytesOrLog(size_t count, std::string* out) {
auto status = ReadRandomBytes(count, *out);
if (status != OK) {
LOG(ERROR) << "Random read failed with status: " << status << std::endl;
return false;
}
return true;
}
bool createSecdiscardable(const std::string& filename, std::string* hash) {
std::string secdiscardable;
if (!readRandomBytesOrLog(SECDISCARDABLE_BYTES, &secdiscardable)) return false;
if (!writeStringToFile(secdiscardable, filename)) return false;
hashWithPrefix(kHashPrefix_secdiscardable, secdiscardable, hash);
return true;
}
bool readSecdiscardable(const std::string& filename, std::string* hash) {
std::string secdiscardable;
if (!readFileToString(filename, &secdiscardable)) return false;
hashWithPrefix(kHashPrefix_secdiscardable, secdiscardable, hash);
return true;
}
static KeymasterOperation begin(Keymaster& keymaster, const std::string& dir,
km::KeyPurpose purpose, const km::AuthorizationSet& keyParams,
const km::AuthorizationSet& opParams,
const km::HardwareAuthToken& authToken,
km::AuthorizationSet* outParams) {
auto kmKeyPath = dir + "/" + kFn_keymaster_key_blob;
std::string kmKey;
if (!readFileToString(kmKeyPath, &kmKey)) return KeymasterOperation();
km::AuthorizationSet inParams(keyParams);
inParams.append(opParams.begin(), opParams.end());
for (;;) {
auto opHandle = keymaster.begin(purpose, kmKey, inParams, authToken, outParams);
if (opHandle) {
return opHandle;
}
if (opHandle.errorCode() != km::ErrorCode::KEY_REQUIRES_UPGRADE) return opHandle;
LOG(DEBUG) << "Upgrading key in memory only: " << dir << std::endl;
std::string newKey;
if (!keymaster.upgradeKey(kmKey, keyParams, &newKey)) return KeymasterOperation();
/*auto newKeyPath = dir + "/" + kFn_keymaster_key_blob_upgraded;
if (!writeStringToFile(newKey, newKeyPath)) return KeymasterOperation();
if (rename(newKeyPath.c_str(), kmKeyPath.c_str()) != 0) {
PLOG(ERROR) << "Unable to move upgraded key to location: " << kmKeyPath;
return KeymasterOperation();
}
if (!keymaster.deleteKey(kmKey)) {
LOG(ERROR) << "Key deletion failed during upgrade, continuing anyway: " << dir;
}*/
kmKey = newKey;
LOG(INFO) << "Key upgraded in memory but not updated in folder: " << dir << std::endl;
}
}
static bool encryptWithKeymasterKey(Keymaster& keymaster, const std::string& dir,
const km::AuthorizationSet& keyParams,
const km::HardwareAuthToken& authToken,
const KeyBuffer& message, std::string* ciphertext) {
km::AuthorizationSet opParams;
km::AuthorizationSet outParams;
auto opHandle =
begin(keymaster, dir, km::KeyPurpose::ENCRYPT, keyParams, opParams, authToken, &outParams);
if (!opHandle) return false;
auto nonceBlob = outParams.GetTagValue(km::TAG_NONCE);
if (!nonceBlob.isOk()) {
LOG(ERROR) << "GCM encryption but no nonce generated" << std::endl;
return false;
}
// nonceBlob here is just a pointer into existing data, must not be freed
std::string nonce(reinterpret_cast<const char*>(&nonceBlob.value()[0]),
nonceBlob.value().size());
if (!checkSize("nonce", nonce.size(), GCM_NONCE_BYTES)) return false;
std::string body;
if (!opHandle.updateCompletely(message, &body)) return false;
std::string mac;
if (!opHandle.finish(&mac)) return false;
if (!checkSize("mac", mac.size(), GCM_MAC_BYTES)) return false;
*ciphertext = nonce + body + mac;
return true;
}
static bool decryptWithKeymasterKey(Keymaster& keymaster, const std::string& dir,
const km::AuthorizationSet& keyParams,
const km::HardwareAuthToken& authToken,
const std::string& ciphertext, KeyBuffer* message) {
auto nonce = ciphertext.substr(0, GCM_NONCE_BYTES);
auto bodyAndMac = ciphertext.substr(GCM_NONCE_BYTES);
auto opParams = km::AuthorizationSetBuilder().Authorization(km::TAG_NONCE,
km::support::blob2hidlVec(nonce));
auto opHandle =
begin(keymaster, dir, km::KeyPurpose::DECRYPT, keyParams, opParams, authToken, nullptr);
if (!opHandle) return false;
if (!opHandle.updateCompletely(bodyAndMac, message)) return false;
if (!opHandle.finish(nullptr)) return false;
return true;
}
static std::string getStretching(const KeyAuthentication& auth) {
if (!auth.usesKeymaster()) {
return kStretch_none;
} else if (auth.secret.empty()) {
return kStretch_nopassword;
} else {
char paramstr[PROPERTY_VALUE_MAX];
property_get(SCRYPT_PROP, paramstr, SCRYPT_DEFAULTS);
return std::string() + kStretchPrefix_scrypt + paramstr;
}
}
static bool stretchingNeedsSalt(const std::string& stretching) {
return stretching != kStretch_nopassword && stretching != kStretch_none;
}
static bool stretchSecret(const std::string& stretching, const std::string& secret,
const std::string& salt, std::string* stretched) {
if (stretching == kStretch_nopassword) {
if (!secret.empty()) {
LOG(WARNING) << "Password present but stretching is nopassword" << std::endl;
// Continue anyway
}
stretched->clear();
} else if (stretching == kStretch_none) {
*stretched = secret;
} else if (std::equal(kStretchPrefix_scrypt.begin(), kStretchPrefix_scrypt.end(),
stretching.begin())) {
int Nf, rf, pf;
if (!parse_scrypt_parameters(stretching.substr(kStretchPrefix_scrypt.size()).c_str(), &Nf,
&rf, &pf)) {
LOG(ERROR) << "Unable to parse scrypt params in stretching: " << stretching << std::endl;
return false;
}
stretched->assign(STRETCHED_BYTES, '\0');
if (crypto_scrypt(reinterpret_cast<const uint8_t*>(secret.data()), secret.size(),
reinterpret_cast<const uint8_t*>(salt.data()), salt.size(), 1 << Nf,
1 << rf, 1 << pf, reinterpret_cast<uint8_t*>(&(*stretched)[0]),
stretched->size()) != 0) {
LOG(ERROR) << "scrypt failed with params: " << stretching << std::endl;
return false;
}
} else {
LOG(ERROR) << "Unknown stretching type: " << stretching << std::endl;
return false;
}
return true;
}
static bool generateAppId(const KeyAuthentication& auth, const std::string& stretching,
const std::string& salt, const std::string& secdiscardable_hash,
std::string* appId) {
std::string stretched;
if (!stretchSecret(stretching, auth.secret, salt, &stretched)) return false;
*appId = secdiscardable_hash + stretched;
return true;
}
static void logOpensslError() {
LOG(ERROR) << "Openssl error: " << ERR_get_error() << std::endl;
}
static bool encryptWithoutKeymaster(const std::string& preKey, const KeyBuffer& plaintext,
std::string* ciphertext) {
std::string key;
hashWithPrefix(kHashPrefix_keygen, preKey, &key);
key.resize(AES_KEY_BYTES);
if (!readRandomBytesOrLog(GCM_NONCE_BYTES, ciphertext)) return false;
auto ctx = std::unique_ptr<EVP_CIPHER_CTX, decltype(&::EVP_CIPHER_CTX_free)>(
EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free);
if (!ctx) {
logOpensslError();
return false;
}
if (1 != EVP_EncryptInit_ex(ctx.get(), EVP_aes_256_gcm(), NULL,
reinterpret_cast<const uint8_t*>(key.data()),
reinterpret_cast<const uint8_t*>(ciphertext->data()))) {
logOpensslError();
return false;
}
ciphertext->resize(GCM_NONCE_BYTES + plaintext.size() + GCM_MAC_BYTES);
int outlen;
if (1 != EVP_EncryptUpdate(
ctx.get(), reinterpret_cast<uint8_t*>(&(*ciphertext)[0] + GCM_NONCE_BYTES),
&outlen, reinterpret_cast<const uint8_t*>(plaintext.data()), plaintext.size())) {
logOpensslError();
return false;
}
if (outlen != static_cast<int>(plaintext.size())) {
LOG(ERROR) << "GCM ciphertext length should be " << plaintext.size() << " was " << outlen << std::endl;
return false;
}
if (1 != EVP_EncryptFinal_ex(
ctx.get(),
reinterpret_cast<uint8_t*>(&(*ciphertext)[0] + GCM_NONCE_BYTES + plaintext.size()),
&outlen)) {
logOpensslError();
return false;
}
if (outlen != 0) {
LOG(ERROR) << "GCM EncryptFinal should be 0, was " << outlen << std::endl;
return false;
}
if (1 != EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_GET_TAG, GCM_MAC_BYTES,
reinterpret_cast<uint8_t*>(&(*ciphertext)[0] + GCM_NONCE_BYTES +
plaintext.size()))) {
logOpensslError();
return false;
}
return true;
}
static bool decryptWithoutKeymaster(const std::string& preKey, const std::string& ciphertext,
KeyBuffer* plaintext) {
if (ciphertext.size() < GCM_NONCE_BYTES + GCM_MAC_BYTES) {
LOG(ERROR) << "GCM ciphertext too small: " << ciphertext.size() << std::endl;
return false;
}
std::string key;
hashWithPrefix(kHashPrefix_keygen, preKey, &key);
key.resize(AES_KEY_BYTES);
auto ctx = std::unique_ptr<EVP_CIPHER_CTX, decltype(&::EVP_CIPHER_CTX_free)>(
EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free);
if (!ctx) {
logOpensslError();
return false;
}
if (1 != EVP_DecryptInit_ex(ctx.get(), EVP_aes_256_gcm(), NULL,
reinterpret_cast<const uint8_t*>(key.data()),
reinterpret_cast<const uint8_t*>(ciphertext.data()))) {
logOpensslError();
return false;
}
*plaintext = KeyBuffer(ciphertext.size() - GCM_NONCE_BYTES - GCM_MAC_BYTES);
int outlen;
if (1 != EVP_DecryptUpdate(ctx.get(), reinterpret_cast<uint8_t*>(&(*plaintext)[0]), &outlen,
reinterpret_cast<const uint8_t*>(ciphertext.data() + GCM_NONCE_BYTES),
plaintext->size())) {
logOpensslError();
return false;
}
if (outlen != static_cast<int>(plaintext->size())) {
LOG(ERROR) << "GCM plaintext length should be " << plaintext->size() << " was " << outlen << std::endl;
return false;
}
if (1 != EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_SET_TAG, GCM_MAC_BYTES,
const_cast<void*>(reinterpret_cast<const void*>(
ciphertext.data() + GCM_NONCE_BYTES + plaintext->size())))) {
logOpensslError();
return false;
}
if (1 != EVP_DecryptFinal_ex(ctx.get(),
reinterpret_cast<uint8_t*>(&(*plaintext)[0] + plaintext->size()),
&outlen)) {
logOpensslError();
return false;
}
if (outlen != 0) {
LOG(ERROR) << "GCM EncryptFinal should be 0, was " << outlen << std::endl;
return false;
}
return true;
}
bool pathExists(const std::string& path) {
return access(path.c_str(), F_OK) == 0;
}
bool storeKey(const std::string& dir, const KeyAuthentication& auth, const KeyBuffer& key) {
if (TEMP_FAILURE_RETRY(mkdir(dir.c_str(), 0700)) == -1) {
PLOG(ERROR) << "key mkdir " << dir << std::endl;
return false;
}
if (!writeStringToFile(kCurrentVersion, dir + "/" + kFn_version)) return false;
std::string secdiscardable_hash;
if (!createSecdiscardable(dir + "/" + kFn_secdiscardable, &secdiscardable_hash)) return false;
std::string stretching = getStretching(auth);
if (!writeStringToFile(stretching, dir + "/" + kFn_stretching)) return false;
std::string salt;
if (stretchingNeedsSalt(stretching)) {
if (ReadRandomBytes(SALT_BYTES, salt) != OK) {
LOG(ERROR) << "Random read failed" << std::endl;
return false;
}
if (!writeStringToFile(salt, dir + "/" + kFn_salt)) return false;
}
std::string appId;
if (!generateAppId(auth, stretching, salt, secdiscardable_hash, &appId)) return false;
std::string encryptedKey;
if (auth.usesKeymaster()) {
Keymaster keymaster;
if (!keymaster) return false;
std::string kmKey;
if (!generateKeymasterKey(keymaster, auth, appId, &kmKey)) return false;
if (!writeStringToFile(kmKey, dir + "/" + kFn_keymaster_key_blob)) return false;
km::AuthorizationSet keyParams;
km::HardwareAuthToken authToken;
std::tie(keyParams, authToken) = beginParams(auth, appId);
if (!encryptWithKeymasterKey(keymaster, dir, keyParams, authToken, key, &encryptedKey))
return false;
} else {
if (!encryptWithoutKeymaster(appId, key, &encryptedKey)) return false;
}
if (!writeStringToFile(encryptedKey, dir + "/" + kFn_encrypted_key)) return false;
return true;
}
bool storeKeyAtomically(const std::string& key_path, const std::string& tmp_path,
const KeyAuthentication& auth, const KeyBuffer& key) {
if (pathExists(key_path)) {
LOG(ERROR) << "Already exists, cannot create key at: " << key_path << std::endl;
return false;
}
if (pathExists(tmp_path)) {
LOG(DEBUG) << "Already exists, destroying: " << tmp_path << std::endl;
destroyKey(tmp_path); // May be partially created so ignore errors
}
if (!storeKey(tmp_path, auth, key)) return false;
if (rename(tmp_path.c_str(), key_path.c_str()) != 0) {
PLOG(ERROR) << "Unable to move new key to location: " << key_path << std::endl;
return false;
}
LOG(DEBUG) << "Created key: " << key_path << std::endl;
return true;
}
bool retrieveKey(const std::string& dir, const KeyAuthentication& auth, KeyBuffer* key) {
std::string version;
if (!readFileToString(dir + "/" + kFn_version, &version)) return false;
if (version != kCurrentVersion) {
LOG(ERROR) << "Version mismatch, expected " << kCurrentVersion << " got " << version << std::endl;
return false;
}
std::string secdiscardable_hash;
if (!readSecdiscardable(dir + "/" + kFn_secdiscardable, &secdiscardable_hash)) return false;
std::string stretching;
if (!readFileToString(dir + "/" + kFn_stretching, &stretching)) return false;
std::string salt;
if (stretchingNeedsSalt(stretching)) {
if (!readFileToString(dir + "/" + kFn_salt, &salt)) return false;
}
std::string appId;
if (!generateAppId(auth, stretching, salt, secdiscardable_hash, &appId)) return false;
std::string encryptedMessage;
if (!readFileToString(dir + "/" + kFn_encrypted_key, &encryptedMessage)) return false;
if (auth.usesKeymaster()) {
Keymaster keymaster;
if (!keymaster) return false;
km::AuthorizationSet keyParams;
km::HardwareAuthToken authToken;
std::tie(keyParams, authToken) = beginParams(auth, appId);
if (!decryptWithKeymasterKey(keymaster, dir, keyParams, authToken, encryptedMessage, key))
return false;
} else {
if (!decryptWithoutKeymaster(appId, encryptedMessage, key)) return false;
}
return true;
}
static bool deleteKey(const std::string& dir) {
LOG(DEBUG) << "not deleting key in " << __FILE__ << std::endl;
return true;
std::string kmKey;
if (!readFileToString(dir + "/" + kFn_keymaster_key_blob, &kmKey)) return false;
Keymaster keymaster;
if (!keymaster) return false;
if (!keymaster.deleteKey(kmKey)) return false;
return true;
}
bool runSecdiscardSingle(const std::string& file) {
if (ForkExecvp(std::vector<std::string>{kSecdiscardPath, "--", file}) != 0) {
LOG(ERROR) << "secdiscard failed" << std::endl;
return false;
}
return true;
}
static bool recursiveDeleteKey(const std::string& dir) {
LOG(DEBUG) << "not recursively deleting key in " << __FILE__ << std::endl;
return true;
if (ForkExecvp(std::vector<std::string>{kRmPath, "-rf", dir}) != 0) {
LOG(ERROR) << "recursive delete failed" << std::endl;
return false;
}
return true;
}
bool destroyKey(const std::string& dir) {
LOG(DEBUG) << "not destroying key in " << __FILE__ << std::endl;
return true;
bool success = true;
// Try each thing, even if previous things failed.
bool uses_km = pathExists(dir + "/" + kFn_keymaster_key_blob);
if (uses_km) {
success &= deleteKey(dir);
}
auto secdiscard_cmd = std::vector<std::string>{
kSecdiscardPath, "--", dir + "/" + kFn_encrypted_key, dir + "/" + kFn_secdiscardable,
};
if (uses_km) {
secdiscard_cmd.emplace_back(dir + "/" + kFn_keymaster_key_blob);
}
if (ForkExecvp(secdiscard_cmd) != 0) {
LOG(ERROR) << "secdiscard failed" << std::endl;
success = false;
}
success &= recursiveDeleteKey(dir);
return success;
}
} // namespace vold
} // namespace android
-86
View File
@@ -1,86 +0,0 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef ANDROID_TWRP_KEYSTORAGE_H
#define ANDROID_TWRP_KEYSTORAGE_H
#include "Keymaster4.h"
#include "KeyBuffer.h"
#include <ext4_utils/ext4_crypt.h>
#include <string>
namespace android {
namespace vold {
namespace km = ::android::hardware::keymaster::V4_0;
// Represents the information needed to decrypt a disk encryption key.
// If "token" is nonempty, it is passed in as a required Gatekeeper auth token.
// If "token" and "secret" are nonempty, "secret" is appended to the application-specific
// binary needed to unlock.
// If only "secret" is nonempty, it is used to decrypt in a non-Keymaster process.
class KeyAuthentication {
public:
KeyAuthentication(std::string t, std::string s) : token{t}, secret{s} {};
bool usesKeymaster() const { return !token.empty() || secret.empty(); };
const std::string token;
const std::string secret;
};
enum class KeyType {
DE_SYS,
DE_USER,
CE_USER
};
extern const KeyAuthentication kEmptyAuthentication;
// Checks if path "path" exists.
bool pathExists(const std::string& path);
bool createSecdiscardable(const std::string& path, std::string* hash);
bool readSecdiscardable(const std::string& path, std::string* hash);
// Create a directory at the named path, and store "key" in it,
// in such a way that it can only be retrieved via Keymaster and
// can be securely deleted.
// It's safe to move/rename the directory after creation.
bool storeKey(const std::string& dir, const KeyAuthentication& auth, const KeyBuffer& key);
// Create a directory at the named path, and store "key" in it as storeKey
// This version creates the key in "tmp_path" then atomically renames "tmp_path"
// to "key_path" thereby ensuring that the key is either stored entirely or
// not at all.
bool storeKeyAtomically(const std::string& key_path, const std::string& tmp_path,
const KeyAuthentication& auth, const KeyBuffer& key);
// Retrieve the key from the named directory.
bool retrieveKey(const std::string& dir, const KeyAuthentication& auth, KeyBuffer* key);
// Securely destroy the key stored in the named directory and delete the directory.
bool destroyKey(const std::string& dir);
bool runSecdiscardSingle(const std::string& file);
bool generateWrappedKey(userid_t user_id, KeyType key_type, KeyBuffer* key);
bool getEphemeralWrappedKey(km::KeyFormat format, KeyBuffer& kmKey, KeyBuffer* key);
} // namespace vold
} // namespace android
#endif
-241
View File
@@ -1,241 +0,0 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "KeyUtil.h"
#include <iomanip>
#include <sstream>
#include <string>
#include <openssl/sha.h>
#include <android-base/file.h>
//#include <android-base/logging.h>
#include <keyutils.h>
#include "KeyStorage4.h"
#include "Ext4CryptPie.h"
#include "Utils.h"
#include <iostream>
#define LOG(x) std::cout
#define PLOG(x) std::cout
#include <sys/types.h>
#include <unistd.h>
#define MAX_USER_ID 0xFFFFFFFF
using android::hardware::keymaster::V4_0::KeyFormat;
using android::vold::KeyType;
namespace android {
namespace vold {
// ext4enc:TODO get this const from somewhere good
const int EXT4_KEY_DESCRIPTOR_SIZE = 8;
// ext4enc:TODO Include structure from somewhere sensible
// MUST be in sync with ext4_crypto.c in kernel
constexpr int EXT4_ENCRYPTION_MODE_AES_256_XTS = 1;
constexpr int EXT4_AES_256_XTS_KEY_SIZE = 64;
constexpr int EXT4_MAX_KEY_SIZE = 64;
struct ext4_encryption_key {
uint32_t mode;
char raw[EXT4_MAX_KEY_SIZE];
uint32_t size;
};
bool randomKey(KeyBuffer* key) {
*key = KeyBuffer(EXT4_AES_256_XTS_KEY_SIZE);
if (ReadRandomBytes(key->size(), key->data()) != 0) {
// TODO status_t plays badly with PLOG, fix it.
LOG(ERROR) << "Random read failed" << std::endl;
return false;
}
return true;
}
// Get raw keyref - used to make keyname and to pass to ioctl
static std::string generateKeyRef(const char* key, int length) {
SHA512_CTX c;
SHA512_Init(&c);
SHA512_Update(&c, key, length);
unsigned char key_ref1[SHA512_DIGEST_LENGTH];
SHA512_Final(key_ref1, &c);
SHA512_Init(&c);
SHA512_Update(&c, key_ref1, SHA512_DIGEST_LENGTH);
unsigned char key_ref2[SHA512_DIGEST_LENGTH];
SHA512_Final(key_ref2, &c);
static_assert(EXT4_KEY_DESCRIPTOR_SIZE <= SHA512_DIGEST_LENGTH,
"Hash too short for descriptor");
return std::string((char*)key_ref2, EXT4_KEY_DESCRIPTOR_SIZE);
}
static bool fillKey(const KeyBuffer& key, ext4_encryption_key* ext4_key) {
if (key.size() != EXT4_AES_256_XTS_KEY_SIZE) {
LOG(ERROR) << "Wrong size key " << key.size();
return false;
}
static_assert(EXT4_AES_256_XTS_KEY_SIZE <= sizeof(ext4_key->raw), "Key too long!");
ext4_key->mode = EXT4_ENCRYPTION_MODE_AES_256_XTS;
ext4_key->size = key.size();
memset(ext4_key->raw, 0, sizeof(ext4_key->raw));
memcpy(ext4_key->raw, key.data(), key.size());
return true;
}
static char const* const NAME_PREFIXES[] = {
"ext4",
"f2fs",
"fscrypt",
nullptr
};
static std::string keyname(const std::string& prefix, const std::string& raw_ref) {
std::ostringstream o;
o << prefix << ":";
for (unsigned char i : raw_ref) {
o << std::hex << std::setw(2) << std::setfill('0') << (int)i;
}
return o.str();
}
// Get the keyring we store all keys in
static bool e4cryptKeyring(key_serial_t* device_keyring) {
*device_keyring = keyctl_search(KEY_SPEC_SESSION_KEYRING, "keyring", "e4crypt", 0);
if (*device_keyring == -1) {
PLOG(ERROR) << "Unable to find device keyring" << std::endl;
return false;
}
return true;
}
// Install password into global keyring
// Return raw key reference for use in policy
bool installKey(const KeyBuffer& key, std::string* raw_ref) {
// Place ext4_encryption_key into automatically zeroing buffer.
KeyBuffer ext4KeyBuffer(sizeof(ext4_encryption_key));
ext4_encryption_key &ext4_key = *reinterpret_cast<ext4_encryption_key*>(ext4KeyBuffer.data());
if (!fillKey(key, &ext4_key)) return false;
if (is_wrapped_key_supported()) {
/* When wrapped key is supported, only the first 32 bytes are
the same per boot. The second 32 bytes can change as the ephemeral
key is different. */
*raw_ref = generateKeyRef(ext4_key.raw, (ext4_key.size)/2);
} else {
*raw_ref = generateKeyRef(ext4_key.raw, ext4_key.size);
}
key_serial_t device_keyring;
if (!e4cryptKeyring(&device_keyring)) return false;
for (char const* const* name_prefix = NAME_PREFIXES; *name_prefix != nullptr; name_prefix++) {
auto ref = keyname(*name_prefix, *raw_ref);
key_serial_t key_id =
add_key("logon", ref.c_str(), (void*)&ext4_key, sizeof(ext4_key), device_keyring);
if (key_id == -1) {
PLOG(ERROR) << "Failed to insert key into keyring " << device_keyring << std::endl;
return false;
}
LOG(DEBUG) << "Added key " << key_id << " (" << ref << ") to keyring " << device_keyring
<< " in process " << getpid() << std::endl;
}
return true;
}
bool evictKey(const std::string& raw_ref) {
LOG(ERROR) << "not actually evicting key\n";
return true;
key_serial_t device_keyring;
if (!e4cryptKeyring(&device_keyring)) return false;
bool success = true;
for (char const* const* name_prefix = NAME_PREFIXES; *name_prefix != nullptr; name_prefix++) {
auto ref = keyname(*name_prefix, raw_ref);
auto key_serial = keyctl_search(device_keyring, "logon", ref.c_str(), 0);
// Unlink the key from the keyring. Prefer unlinking to revoking or
// invalidating, since unlinking is actually no less secure currently, and
// it avoids bugs in certain kernel versions where the keyring key is
// referenced from places it shouldn't be.
if (keyctl_unlink(key_serial, device_keyring) != 0) {
PLOG(ERROR) << "Failed to unlink key with serial " << key_serial << " ref " << ref;
success = false;
} else {
LOG(DEBUG) << "Unlinked key with serial " << key_serial << " ref " << ref;
}
}
return success;
}
bool retrieveAndInstallKey(bool create_if_absent, const KeyAuthentication& key_authentication,
const std::string& key_path, const std::string& tmp_path,
std::string* key_ref, bool wrapped_key_supported) {
KeyBuffer key;
if (pathExists(key_path)) {
LOG(DEBUG) << "Key exists, using: " << key_path << std::endl;
if (!retrieveKey(key_path, key_authentication, &key)) return false;
} else {
if (!create_if_absent) {
LOG(ERROR) << "No key found in " << key_path << std::endl;
return false;
}
LOG(INFO) << "Creating new key in " << key_path << std::endl;
if (wrapped_key_supported) {
if(!generateWrappedKey(MAX_USER_ID, KeyType::DE_SYS, &key)) return false;
} else {
if (!randomKey(&key)) return false;
}
if (!storeKeyAtomically(key_path, tmp_path, key_authentication, key)) return false;
}
if (wrapped_key_supported) {
KeyBuffer ephemeral_wrapped_key;
if (!getEphemeralWrappedKey(KeyFormat::RAW, key, &ephemeral_wrapped_key)) {
LOG(ERROR) << "Failed to export key in retrieveAndInstallKey";
return false;
}
key = std::move(ephemeral_wrapped_key);
}
if (!installKey(key, key_ref)) {
LOG(ERROR) << "Failed to install key in " << key_path << std::endl;
return false;
}
return true;
}
bool retrieveKey(bool create_if_absent, const std::string& key_path,
const std::string& tmp_path, KeyBuffer* key) {
if (pathExists(key_path)) {
LOG(DEBUG) << "Key exists, using: " << key_path << std::endl;
if (!retrieveKey(key_path, kEmptyAuthentication, key)) return false;
} else {
if (!create_if_absent) {
LOG(ERROR) << "No key found in " << key_path << std::endl;
return false;
}
LOG(INFO) << "Creating new key in " << key_path << std::endl;
if (!randomKey(key)) return false;
if (!storeKeyAtomically(key_path, tmp_path,
kEmptyAuthentication, *key)) return false;
}
return true;
}
} // namespace vold
} // namespace android
-42
View File
@@ -1,42 +0,0 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef ANDROID_VOLD_KEYUTIL_H
#define ANDROID_VOLD_KEYUTIL_H
#include "KeyBuffer.h"
#include "KeyStorage4.h"
#include "Keymaster4.h"
#include <string>
#include <memory>
namespace android {
namespace vold {
bool randomKey(KeyBuffer* key);
bool installKey(const KeyBuffer& key, std::string* raw_ref);
bool evictKey(const std::string& raw_ref);
bool retrieveAndInstallKey(bool create_if_absent, const KeyAuthentication& key_authentication,
const std::string& key_path, const std::string& tmp_path,
std::string* key_ref, bool wrapped_key_supported);
bool retrieveKey(bool create_if_absent, const std::string& key_path,
const std::string& tmp_path, KeyBuffer* key);
} // namespace vold
} // namespace android
#endif
-254
View File
@@ -1,254 +0,0 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "Keymaster.h"
//#include <android-base/logging.h>
#include <hardware/hardware.h>
#include <hardware/keymaster1.h>
#include <hardware/keymaster2.h>
#include <iostream>
#define ERROR 1
#define LOG(x) std::cout
namespace android {
namespace vold {
class IKeymasterDevice {
public:
IKeymasterDevice() {}
virtual ~IKeymasterDevice() {}
/*virtual keymaster_error_t generate_key(const keymaster_key_param_set_t* params,
keymaster_key_blob_t* key_blob) const = 0;*/
virtual keymaster_error_t delete_key(const keymaster_key_blob_t* key) const = 0;
virtual keymaster_error_t begin(keymaster_purpose_t purpose, const keymaster_key_blob_t* key,
const keymaster_key_param_set_t* in_params,
keymaster_key_param_set_t* out_params,
keymaster_operation_handle_t* operation_handle) const = 0;
virtual keymaster_error_t update(keymaster_operation_handle_t operation_handle,
const keymaster_key_param_set_t* in_params,
const keymaster_blob_t* input, size_t* input_consumed,
keymaster_key_param_set_t* out_params,
keymaster_blob_t* output) const = 0;
virtual keymaster_error_t finish(keymaster_operation_handle_t operation_handle,
const keymaster_key_param_set_t* in_params,
const keymaster_blob_t* signature,
keymaster_key_param_set_t* out_params,
keymaster_blob_t* output) const = 0;
virtual keymaster_error_t abort(keymaster_operation_handle_t operation_handle) const = 0;
protected:
DISALLOW_COPY_AND_ASSIGN(IKeymasterDevice);
};
template <typename T> class KeymasterDevice : public IKeymasterDevice {
public:
KeymasterDevice(T* d) : mDevice{d} {}
/*keymaster_error_t generate_key(const keymaster_key_param_set_t* params,
keymaster_key_blob_t* key_blob) const override final {
return mDevice->generate_key(mDevice, params, key_blob, nullptr);
}*/
keymaster_error_t delete_key(const keymaster_key_blob_t* key) const override final {
if (mDevice->delete_key == nullptr) return KM_ERROR_OK;
return mDevice->delete_key(mDevice, key);
}
keymaster_error_t begin(keymaster_purpose_t purpose, const keymaster_key_blob_t* key,
const keymaster_key_param_set_t* in_params,
keymaster_key_param_set_t* out_params,
keymaster_operation_handle_t* operation_handle) const override final {
return mDevice->begin(mDevice, purpose, key, in_params, out_params, operation_handle);
}
keymaster_error_t update(keymaster_operation_handle_t operation_handle,
const keymaster_key_param_set_t* in_params,
const keymaster_blob_t* input, size_t* input_consumed,
keymaster_key_param_set_t* out_params,
keymaster_blob_t* output) const override final {
return mDevice->update(mDevice, operation_handle, in_params, input, input_consumed,
out_params, output);
}
keymaster_error_t abort(keymaster_operation_handle_t operation_handle) const override final {
return mDevice->abort(mDevice, operation_handle);
}
protected:
T* const mDevice;
};
class Keymaster1Device : public KeymasterDevice<keymaster1_device_t> {
public:
Keymaster1Device(keymaster1_device_t* d) : KeymasterDevice<keymaster1_device_t>{d} {}
~Keymaster1Device() override final { keymaster1_close(mDevice); }
keymaster_error_t finish(keymaster_operation_handle_t operation_handle,
const keymaster_key_param_set_t* in_params,
const keymaster_blob_t* signature,
keymaster_key_param_set_t* out_params,
keymaster_blob_t* output) const override final {
return mDevice->finish(mDevice, operation_handle, in_params, signature, out_params, output);
}
};
class Keymaster2Device : public KeymasterDevice<keymaster2_device_t> {
public:
Keymaster2Device(keymaster2_device_t* d) : KeymasterDevice<keymaster2_device_t>{d} {}
~Keymaster2Device() override final { keymaster2_close(mDevice); }
keymaster_error_t finish(keymaster_operation_handle_t operation_handle,
const keymaster_key_param_set_t* in_params,
const keymaster_blob_t* signature,
keymaster_key_param_set_t* out_params,
keymaster_blob_t* output) const override final {
return mDevice->finish(mDevice, operation_handle, in_params, nullptr, signature, out_params,
output);
}
};
KeymasterOperation::~KeymasterOperation() {
if (mDevice) mDevice->abort(mOpHandle);
}
bool KeymasterOperation::updateCompletely(const std::string& input, std::string* output) {
output->clear();
auto it = input.begin();
while (it != input.end()) {
size_t toRead = static_cast<size_t>(input.end() - it);
keymaster_blob_t inputBlob{reinterpret_cast<const uint8_t*>(&*it), toRead};
keymaster_blob_t outputBlob;
size_t inputConsumed;
auto error =
mDevice->update(mOpHandle, nullptr, &inputBlob, &inputConsumed, nullptr, &outputBlob);
if (error != KM_ERROR_OK) {
LOG(ERROR) << "update failed, code " << error;
mDevice = nullptr;
return false;
}
output->append(reinterpret_cast<const char*>(outputBlob.data), outputBlob.data_length);
free(const_cast<uint8_t*>(outputBlob.data));
if (inputConsumed > toRead) {
LOG(ERROR) << "update reported too much input consumed";
mDevice = nullptr;
return false;
}
it += inputConsumed;
}
return true;
}
bool KeymasterOperation::finish() {
auto error = mDevice->finish(mOpHandle, nullptr, nullptr, nullptr, nullptr);
mDevice = nullptr;
if (error != KM_ERROR_OK) {
LOG(ERROR) << "finish failed, code " << error;
return false;
}
return true;
}
bool KeymasterOperation::finishWithOutput(std::string* output) {
keymaster_blob_t outputBlob;
auto error = mDevice->finish(mOpHandle, nullptr, nullptr, nullptr, &outputBlob);
mDevice = nullptr;
if (error != KM_ERROR_OK) {
LOG(ERROR) << "finish failed, code " << error;
return false;
}
output->assign(reinterpret_cast<const char*>(outputBlob.data), outputBlob.data_length);
free(const_cast<uint8_t*>(outputBlob.data));
return true;
}
Keymaster::Keymaster() {
mDevice = nullptr;
const hw_module_t* module;
int ret = hw_get_module_by_class(KEYSTORE_HARDWARE_MODULE_ID, NULL, &module);
if (ret != 0) {
LOG(ERROR) << "hw_get_module_by_class returned " << ret;
return;
}
if (module->module_api_version == KEYMASTER_MODULE_API_VERSION_1_0) {
keymaster1_device_t* device;
ret = keymaster1_open(module, &device);
if (ret != 0) {
LOG(ERROR) << "keymaster1_open returned " << ret;
return;
}
mDevice = std::make_shared<Keymaster1Device>(device);
} else if (module->module_api_version == KEYMASTER_MODULE_API_VERSION_2_0) {
keymaster2_device_t* device;
ret = keymaster2_open(module, &device);
if (ret != 0) {
LOG(ERROR) << "keymaster2_open returned " << ret;
return;
}
mDevice = std::make_shared<Keymaster2Device>(device);
} else {
LOG(ERROR) << "module_api_version is " << module->module_api_version;
return;
}
}
/*bool Keymaster::generateKey(const keymaster::AuthorizationSet& inParams, std::string* key) {
keymaster_key_blob_t keyBlob;
auto error = mDevice->generate_key(&inParams, &keyBlob);
if (error != KM_ERROR_OK) {
LOG(ERROR) << "generate_key failed, code " << error;
return false;
}
key->assign(reinterpret_cast<const char*>(keyBlob.key_material), keyBlob.key_material_size);
free(const_cast<uint8_t*>(keyBlob.key_material));
return true;
}*/
bool Keymaster::deleteKey(const std::string& key) {
keymaster_key_blob_t keyBlob{reinterpret_cast<const uint8_t*>(key.data()), key.size()};
auto error = mDevice->delete_key(&keyBlob);
if (error != KM_ERROR_OK) {
LOG(ERROR) << "delete_key failed, code " << error;
return false;
}
return true;
}
KeymasterOperation Keymaster::begin(keymaster_purpose_t purpose, const std::string& key,
const keymaster::AuthorizationSet& inParams,
keymaster::AuthorizationSet* outParams) {
keymaster_key_blob_t keyBlob{reinterpret_cast<const uint8_t*>(key.data()), key.size()};
keymaster_operation_handle_t mOpHandle;
keymaster_key_param_set_t outParams_set;
auto error = mDevice->begin(purpose, &keyBlob, &inParams, &outParams_set, &mOpHandle);
if (error != KM_ERROR_OK) {
LOG(ERROR) << "begin failed, code " << error;
return KeymasterOperation(nullptr, mOpHandle);
}
outParams->Clear();
outParams->push_back(outParams_set);
keymaster_free_param_set(&outParams_set);
return KeymasterOperation(mDevice, mOpHandle);
}
KeymasterOperation Keymaster::begin(keymaster_purpose_t purpose, const std::string& key,
const keymaster::AuthorizationSet& inParams) {
keymaster_key_blob_t keyBlob{reinterpret_cast<const uint8_t*>(key.data()), key.size()};
keymaster_operation_handle_t mOpHandle;
auto error = mDevice->begin(purpose, &keyBlob, &inParams, nullptr, &mOpHandle);
if (error != KM_ERROR_OK) {
LOG(ERROR) << "begin failed, code " << error;
return KeymasterOperation(nullptr, mOpHandle);
}
return KeymasterOperation(mDevice, mOpHandle);
}
} // namespace vold
} // namespace android
-111
View File
@@ -1,111 +0,0 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef ANDROID_VOLD_KEYMASTER_H
#define ANDROID_VOLD_KEYMASTER_H
#include <memory>
#include <string>
#include <utility>
#include <keymaster/authorization_set.h>
#include "Utils.h"
namespace android {
namespace vold {
using namespace keymaster;
// C++ wrappers to the Keymaster C interface.
// This is tailored to the needs of KeyStorage, but could be extended to be
// a more general interface.
// Class that wraps a keymaster1_device_t or keymaster2_device_t and provides methods
// they have in common. Also closes the device on destruction.
class IKeymasterDevice;
// Wrapper for a keymaster_operation_handle_t representing an
// ongoing Keymaster operation. Aborts the operation
// in the destructor if it is unfinished. Methods log failures
// to LOG(ERROR).
class KeymasterOperation {
public:
~KeymasterOperation();
// Is this instance valid? This is false if creation fails, and becomes
// false on finish or if an update fails.
explicit operator bool() { return mDevice != nullptr; }
// Call "update" repeatedly until all of the input is consumed, and
// concatenate the output. Return true on success.
bool updateCompletely(const std::string& input, std::string* output);
// Finish; pass nullptr for the "output" param.
bool finish();
// Finish and write the output to this string.
bool finishWithOutput(std::string* output);
// Move constructor
KeymasterOperation(KeymasterOperation&& rhs) {
mOpHandle = std::move(rhs.mOpHandle);
mDevice = std::move(rhs.mDevice);
}
private:
KeymasterOperation(std::shared_ptr<IKeymasterDevice> d, keymaster_operation_handle_t h)
: mDevice{d}, mOpHandle{h} {}
std::shared_ptr<IKeymasterDevice> mDevice;
keymaster_operation_handle_t mOpHandle;
DISALLOW_COPY_AND_ASSIGN(KeymasterOperation);
friend class Keymaster;
};
// Wrapper for a Keymaster device for methods that start a KeymasterOperation or are not
// part of one.
class Keymaster {
public:
Keymaster();
// false if we failed to open the keymaster device.
explicit operator bool() { return mDevice != nullptr; }
// Generate a key in the keymaster from the given params.
//bool generateKey(const AuthorizationSet& inParams, std::string* key);
// If the keymaster supports it, permanently delete a key.
bool deleteKey(const std::string& key);
// Begin a new cryptographic operation, collecting output parameters.
KeymasterOperation begin(keymaster_purpose_t purpose, const std::string& key,
const AuthorizationSet& inParams, AuthorizationSet* outParams);
// Begin a new cryptographic operation; don't collect output parameters.
KeymasterOperation begin(keymaster_purpose_t purpose, const std::string& key,
const AuthorizationSet& inParams);
private:
std::shared_ptr<IKeymasterDevice> mDevice;
DISALLOW_COPY_AND_ASSIGN(Keymaster);
};
template <keymaster_tag_t Tag>
inline AuthorizationSetBuilder& addStringParam(AuthorizationSetBuilder&& params,
TypedTag<KM_BYTES, Tag> tag,
const std::string& val) {
return params.Authorization(tag, val.data(), val.size());
}
template <keymaster_tag_t Tag>
inline void addStringParam(AuthorizationSetBuilder* params, TypedTag<KM_BYTES, Tag> tag,
const std::string& val) {
params->Authorization(tag, val.data(), val.size());
}
} // namespace vold
} // namespace android
#endif
-348
View File
@@ -1,348 +0,0 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "Keymaster3.h"
//#include <android-base/logging.h>
#include <keystore/keymaster_tags.h>
#include <keystore/authorization_set.h>
#include <keystore/keystore_hidl_support.h>
#include <iostream>
#define ERROR 1
#define LOG(x) std::cout
using namespace ::keystore;
using android::hardware::hidl_string;
namespace android {
namespace vold {
KeymasterOperation::~KeymasterOperation() {
if (mDevice.get()) mDevice->abort(mOpHandle);
}
bool KeymasterOperation::updateCompletely(const std::string& input, std::string* output) {
if (output)
output->clear();
auto it = input.begin();
uint32_t inputConsumed;
ErrorCode km_error;
auto hidlCB = [&] (ErrorCode ret, uint32_t _inputConsumed,
const hidl_vec<KeyParameter>& /*ignored*/, const hidl_vec<uint8_t>& _output) {
km_error = ret;
if (km_error != ErrorCode::OK) return;
inputConsumed = _inputConsumed;
if (output)
output->append(reinterpret_cast<const char*>(&_output[0]), _output.size());
};
while (it != input.end()) {
size_t toRead = static_cast<size_t>(input.end() - it);
auto inputBlob = blob2hidlVec(reinterpret_cast<const uint8_t*>(&*it), toRead);
auto error = mDevice->update(mOpHandle, hidl_vec<KeyParameter>(), inputBlob, hidlCB);
if (!error.isOk()) {
LOG(ERROR) << "update failed: " << error.description();
mDevice = nullptr;
return false;
}
if (km_error != ErrorCode::OK) {
LOG(ERROR) << "update failed, code " << int32_t(km_error);
mDevice = nullptr;
return false;
}
if (inputConsumed > toRead) {
LOG(ERROR) << "update reported too much input consumed";
mDevice = nullptr;
return false;
}
it += inputConsumed;
}
return true;
}
bool KeymasterOperation::finish(std::string* output) {
ErrorCode km_error;
auto hidlCb = [&] (ErrorCode ret, const hidl_vec<KeyParameter>& /*ignored*/,
const hidl_vec<uint8_t>& _output) {
km_error = ret;
if (km_error != ErrorCode::OK) return;
if (output)
output->assign(reinterpret_cast<const char*>(&_output[0]), _output.size());
};
auto error = mDevice->finish(mOpHandle, hidl_vec<KeyParameter>(), hidl_vec<uint8_t>(),
hidl_vec<uint8_t>(), hidlCb);
mDevice = nullptr;
if (!error.isOk()) {
LOG(ERROR) << "finish failed: " << error.description();
return false;
}
if (km_error != ErrorCode::OK) {
LOG(ERROR) << "finish failed, code " << int32_t(km_error);
return false;
}
return true;
}
Keymaster::Keymaster() {
mDevice = ::android::hardware::keymaster::V3_0::IKeymasterDevice::getService();
}
/*bool Keymaster::generateKey(const AuthorizationSet& inParams, std::string* key) {
ErrorCode km_error;
auto hidlCb = [&] (ErrorCode ret, const hidl_vec<uint8_t>& keyBlob,
const KeyCharacteristics& /*ignored* /) {
km_error = ret;
if (km_error != ErrorCode::OK) return;
if (key)
key->assign(reinterpret_cast<const char*>(&keyBlob[0]), keyBlob.size());
};
auto error = mDevice->generateKey(inParams.hidl_data(), hidlCb);
if (!error.isOk()) {
LOG(ERROR) << "generate_key failed: " << error.description();
return false;
}
if (km_error != ErrorCode::OK) {
LOG(ERROR) << "generate_key failed, code " << int32_t(km_error);
return false;
}
return true;
}*/
bool Keymaster::deleteKey(const std::string& key) {
LOG(ERROR) << "NOT deleting key in TWRP";
return false;
/*auto keyBlob = blob2hidlVec(key);
auto error = mDevice->deleteKey(keyBlob);
if (!error.isOk()) {
LOG(ERROR) << "delete_key failed: " << error.description();
return false;
}
if (ErrorCode(error) != ErrorCode::OK) {
LOG(ERROR) << "delete_key failed, code " << uint32_t(ErrorCode(error));
return false;
}
return true;*/
}
bool Keymaster::upgradeKey(const std::string& oldKey, const AuthorizationSet& inParams,
std::string* newKey) {
auto oldKeyBlob = blob2hidlVec(oldKey);
ErrorCode km_error;
auto hidlCb = [&] (ErrorCode ret, const hidl_vec<uint8_t>& upgradedKeyBlob) {
km_error = ret;
if (km_error != ErrorCode::OK) return;
if (newKey)
newKey->assign(reinterpret_cast<const char*>(&upgradedKeyBlob[0]),
upgradedKeyBlob.size());
};
auto error = mDevice->upgradeKey(oldKeyBlob, inParams.hidl_data(), hidlCb);
if (!error.isOk()) {
LOG(ERROR) << "upgrade_key failed: " << error.description();
return false;
}
if (km_error != ErrorCode::OK) {
LOG(ERROR) << "upgrade_key failed, code " << int32_t(km_error);
return false;
}
return true;
}
KeymasterOperation Keymaster::begin(KeyPurpose purpose, const std::string& key,
const AuthorizationSet& inParams,
AuthorizationSet* outParams) {
auto keyBlob = blob2hidlVec(key);
uint64_t mOpHandle;
ErrorCode km_error;
auto hidlCb = [&] (ErrorCode ret, const hidl_vec<KeyParameter>& _outParams,
uint64_t operationHandle) {
km_error = ret;
if (km_error != ErrorCode::OK) return;
if (outParams)
*outParams = _outParams;
mOpHandle = operationHandle;
};
auto error = mDevice->begin(purpose, keyBlob, inParams.hidl_data(), hidlCb);
if (!error.isOk()) {
LOG(ERROR) << "begin failed: " << error.description();
return KeymasterOperation(ErrorCode::UNKNOWN_ERROR);
}
if (km_error != ErrorCode::OK) {
LOG(ERROR) << "begin failed, code " << int32_t(km_error);
return KeymasterOperation(km_error);
}
return KeymasterOperation(mDevice, mOpHandle);
}
bool Keymaster::isSecure() {
bool _isSecure = false;
auto rc = mDevice->getHardwareFeatures(
[&] (bool isSecure, bool, bool, bool, bool, const hidl_string&, const hidl_string&) {
_isSecure = isSecure; });
return rc.isOk() && _isSecure;
}
} // namespace vold
} // namespace android
using namespace ::android::vold;
/*
int keymaster_compatibility_cryptfs_scrypt() {
Keymaster dev;
if (!dev) {
LOG(ERROR) << "Failed to initiate keymaster session";
return -1;
}
return dev.isSecure();
}
*/
/*int keymaster_create_key_for_cryptfs_scrypt(uint32_t rsa_key_size,
uint64_t rsa_exponent,
uint32_t ratelimit,
uint8_t* key_buffer,
uint32_t key_buffer_size,
uint32_t* key_out_size)
{
Keymaster dev;
std::string key;
if (!dev) {
LOG(ERROR) << "Failed to initiate keymaster session";
return -1;
}
if (!key_buffer || !key_out_size) {
LOG(ERROR) << __FILE__ << ":" << __LINE__ << ":Invalid argument";
return -1;
}
if (key_out_size) {
*key_out_size = 0;
}
auto paramBuilder = AuthorizationSetBuilder()
.Authorization(TAG_ALGORITHM, Algorithm::RSA)
.Authorization(TAG_KEY_SIZE, rsa_key_size)
.Authorization(TAG_RSA_PUBLIC_EXPONENT, rsa_exponent)
.Authorization(TAG_PURPOSE, KeyPurpose::SIGN)
.Authorization(TAG_PADDING, PaddingMode::NONE)
.Authorization(TAG_DIGEST, Digest::NONE)
.Authorization(TAG_BLOB_USAGE_REQUIREMENTS,
KeyBlobUsageRequirements::STANDALONE)
.Authorization(TAG_NO_AUTH_REQUIRED)
.Authorization(TAG_MIN_SECONDS_BETWEEN_OPS, ratelimit);
if (!dev.generateKey(paramBuilder, &key)) {
return -1;
}
if (key_out_size) {
*key_out_size = key.size();
}
if (key_buffer_size < key.size()) {
return -1;
}
std::copy(key.data(), key.data() + key.size(), key_buffer);
return 0;
}*/
int keymaster_sign_object_for_cryptfs_scrypt(const uint8_t* key_blob,
size_t key_blob_size,
uint32_t ratelimit,
const uint8_t* object,
const size_t object_size,
uint8_t** signature_buffer,
size_t* signature_buffer_size,
uint8_t* key_buffer,
uint32_t key_buffer_size,
uint32_t* key_out_size)
{
Keymaster dev;
if (!dev) {
LOG(ERROR) << "Failed to initiate keymaster session";
return -1;
}
if (!key_blob || !object || !signature_buffer || !signature_buffer_size) {
LOG(ERROR) << __FILE__ << ":" << __LINE__ << ":Invalid argument";
return -1;
}
AuthorizationSet outParams;
std::string key(reinterpret_cast<const char*>(key_blob), key_blob_size);
std::string input(reinterpret_cast<const char*>(object), object_size);
std::string output;
KeymasterOperation op;
auto paramBuilder = AuthorizationSetBuilder()
.Authorization(TAG_PADDING, PaddingMode::NONE)
.Authorization(TAG_DIGEST, Digest::NONE);
while (true) {
op = dev.begin(KeyPurpose::SIGN, key, paramBuilder, &outParams);
if (op.errorCode() == ErrorCode::KEY_RATE_LIMIT_EXCEEDED) {
sleep(ratelimit);
continue;
} else if (op.errorCode() == ErrorCode::KEY_REQUIRES_UPGRADE) {
std::string newKey;
bool ret = dev.upgradeKey(key, paramBuilder, &newKey);
if(ret == false) {
LOG(ERROR) << "Error upgradeKey: ";
return -1;
}
if (key_out_size) {
*key_out_size = newKey.size();
}
if (key_buffer_size < newKey.size()) {
LOG(ERROR) << "key buffer size is too small";
return -1;
}
std::copy(newKey.data(), newKey.data() + newKey.size(), key_buffer);
key = newKey;
} else break;
}
if (op.errorCode() != ErrorCode::OK) {
LOG(ERROR) << "Error starting keymaster signature transaction: " << int32_t(op.errorCode());
return -1;
}
if (!op.updateCompletely(input, &output)) {
LOG(ERROR) << "Error sending data to keymaster signature transaction: "
<< uint32_t(op.errorCode());
return -1;
}
if (!op.finish(&output)) {
LOG(ERROR) << "Error finalizing keymaster signature transaction: " << int32_t(op.errorCode());
return -1;
}
*signature_buffer = reinterpret_cast<uint8_t*>(malloc(output.size()));
if (*signature_buffer == nullptr) {
LOG(ERROR) << "Error allocation buffer for keymaster signature";
return -1;
}
*signature_buffer_size = output.size();
std::copy(output.data(), output.data() + output.size(), *signature_buffer);
return 0;
}
-151
View File
@@ -1,151 +0,0 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef ANDROID_VOLD_KEYMASTER_H
#define ANDROID_VOLD_KEYMASTER_H
#ifdef __cplusplus
#include <memory>
#include <string>
#include <utility>
#include <android/hardware/keymaster/3.0/IKeymasterDevice.h>
#include <keystore/authorization_set.h>
#include "Utils.h"
namespace android {
namespace vold {
using ::android::hardware::keymaster::V3_0::IKeymasterDevice;
using ::keystore::ErrorCode;
using ::keystore::KeyPurpose;
using ::keystore::AuthorizationSet;
// C++ wrappers to the Keymaster hidl interface.
// This is tailored to the needs of KeyStorage, but could be extended to be
// a more general interface.
// Wrapper for a Keymaster operation handle representing an
// ongoing Keymaster operation. Aborts the operation
// in the destructor if it is unfinished. Methods log failures
// to LOG(ERROR).
class KeymasterOperation {
public:
~KeymasterOperation();
// Is this instance valid? This is false if creation fails, and becomes
// false on finish or if an update fails.
explicit operator bool() { return mError == ErrorCode::OK; }
ErrorCode errorCode() { return mError; }
// Call "update" repeatedly until all of the input is consumed, and
// concatenate the output. Return true on success.
bool updateCompletely(const std::string& input, std::string* output);
// Finish and write the output to this string, unless pointer is null.
bool finish(std::string* output);
// Move constructor
KeymasterOperation(KeymasterOperation&& rhs) {
mDevice = std::move(rhs.mDevice);
mOpHandle = std::move(rhs.mOpHandle);
mError = std::move(rhs.mError);
}
// Construct an object in an error state for error returns
KeymasterOperation()
: mDevice{nullptr}, mOpHandle{0},
mError {ErrorCode::UNKNOWN_ERROR} {}
// Move Assignment
KeymasterOperation& operator= (KeymasterOperation&& rhs) {
mDevice = std::move(rhs.mDevice);
mOpHandle = std::move(rhs.mOpHandle);
mError = std::move(rhs.mError);
rhs.mError = ErrorCode::UNKNOWN_ERROR;
rhs.mOpHandle = 0;
return *this;
}
private:
KeymasterOperation(const sp<IKeymasterDevice>& d, uint64_t h)
: mDevice{d}, mOpHandle{h}, mError {ErrorCode::OK} {}
KeymasterOperation(ErrorCode error)
: mDevice{nullptr}, mOpHandle{0},
mError {error} {}
sp<IKeymasterDevice> mDevice;
uint64_t mOpHandle;
ErrorCode mError;
DISALLOW_COPY_AND_ASSIGN(KeymasterOperation);
friend class Keymaster;
};
// Wrapper for a Keymaster device for methods that start a KeymasterOperation or are not
// part of one.
class Keymaster {
public:
Keymaster();
// false if we failed to open the keymaster device.
explicit operator bool() { return mDevice.get() != nullptr; }
// Generate a key in the keymaster from the given params.
//bool generateKey(const AuthorizationSet& inParams, std::string* key);
// If the keymaster supports it, permanently delete a key.
bool deleteKey(const std::string& key);
// Replace stored key blob in response to KM_ERROR_KEY_REQUIRES_UPGRADE.
bool upgradeKey(const std::string& oldKey, const AuthorizationSet& inParams,
std::string* newKey);
// Begin a new cryptographic operation, collecting output parameters if pointer is non-null
KeymasterOperation begin(KeyPurpose purpose, const std::string& key,
const AuthorizationSet& inParams, AuthorizationSet* outParams);
bool isSecure();
private:
sp<hardware::keymaster::V3_0::IKeymasterDevice> mDevice;
DISALLOW_COPY_AND_ASSIGN(Keymaster);
};
} // namespace vold
} // namespace android
#endif // __cplusplus
/*
* The following functions provide C bindings to keymaster services
* needed by cryptfs scrypt. The compatibility check checks whether
* the keymaster implementation is considered secure, i.e., TEE backed.
* The create_key function generates an RSA key for signing.
* The sign_object function signes an object with the given keymaster
* key.
*/
__BEGIN_DECLS
//int keymaster_compatibility_cryptfs_scrypt();
/*int keymaster_create_key_for_cryptfs_scrypt(uint32_t rsa_key_size,
uint64_t rsa_exponent,
uint32_t ratelimit,
uint8_t* key_buffer,
uint32_t key_buffer_size,
uint32_t* key_out_size);*/
int keymaster_sign_object_for_cryptfs_scrypt(const uint8_t* key_blob,
size_t key_blob_size,
uint32_t ratelimit,
const uint8_t* object,
const size_t object_size,
uint8_t** signature_buffer,
size_t* signature_buffer_size,
uint8_t* key_buffer,
uint32_t key_buffer_size,
uint32_t* key_out_size);
__END_DECLS
#endif
-382
View File
@@ -1,382 +0,0 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "Keymaster4.h"
//#include <android-base/logging.h>
#include <keymasterV4_0/authorization_set.h>
#include <keymasterV4_0/keymaster_utils.h>
#include <iostream>
#define LOG(x) std::cout
#define PLOG(x) std::cout
namespace android {
namespace vold {
using ::android::hardware::hidl_string;
using ::android::hardware::hidl_vec;
using ::android::hardware::keymaster::V4_0::SecurityLevel;
KeymasterOperation::~KeymasterOperation() {
if (mDevice) mDevice->abort(mOpHandle);
}
bool KeymasterOperation::updateCompletely(const char* input, size_t inputLen,
const std::function<void(const char*, size_t)> consumer) {
uint32_t inputConsumed = 0;
km::ErrorCode km_error;
auto hidlCB = [&](km::ErrorCode ret, uint32_t inputConsumedDelta,
const hidl_vec<km::KeyParameter>& /*ignored*/,
const hidl_vec<uint8_t>& _output) {
km_error = ret;
if (km_error != km::ErrorCode::OK) return;
inputConsumed += inputConsumedDelta;
consumer(reinterpret_cast<const char*>(&_output[0]), _output.size());
};
while (inputConsumed != inputLen) {
size_t toRead = static_cast<size_t>(inputLen - inputConsumed);
auto inputBlob = km::support::blob2hidlVec(
reinterpret_cast<const uint8_t*>(&input[inputConsumed]), toRead);
auto error = mDevice->update(mOpHandle, hidl_vec<km::KeyParameter>(), inputBlob,
km::HardwareAuthToken(), km::VerificationToken(), hidlCB);
if (!error.isOk()) {
LOG(ERROR) << "update failed: " << error.description() << std::endl;
mDevice = nullptr;
return false;
}
if (km_error != km::ErrorCode::OK) {
LOG(ERROR) << "update failed, code " << int32_t(km_error) << std::endl;
mDevice = nullptr;
return false;
}
if (inputConsumed > inputLen) {
LOG(ERROR) << "update reported too much input consumed" << std::endl;
mDevice = nullptr;
return false;
}
}
return true;
}
bool KeymasterOperation::finish(std::string* output) {
km::ErrorCode km_error;
auto hidlCb = [&](km::ErrorCode ret, const hidl_vec<km::KeyParameter>& /*ignored*/,
const hidl_vec<uint8_t>& _output) {
km_error = ret;
if (km_error != km::ErrorCode::OK) return;
if (output) output->assign(reinterpret_cast<const char*>(&_output[0]), _output.size());
};
auto error = mDevice->finish(mOpHandle, hidl_vec<km::KeyParameter>(), hidl_vec<uint8_t>(),
hidl_vec<uint8_t>(), km::HardwareAuthToken(),
km::VerificationToken(), hidlCb);
mDevice = nullptr;
if (!error.isOk()) {
LOG(ERROR) << "finish failed: " << error.description() << std::endl;
return false;
}
if (km_error != km::ErrorCode::OK) {
LOG(ERROR) << "finish failed, code " << int32_t(km_error) << std::endl;
return false;
}
return true;
}
/* static */ bool Keymaster::hmacKeyGenerated = false;
Keymaster::Keymaster() {
auto devices = KmDevice::enumerateAvailableDevices();
if (!hmacKeyGenerated) {
KmDevice::performHmacKeyAgreement(devices);
hmacKeyGenerated = true;
}
for (auto& dev : devices) {
// Do not use StrongBox for device encryption / credential encryption. If a security chip
// is present it will have Weaver, which already strengthens CE. We get no additional
// benefit from using StrongBox here, so skip it.
if (dev->halVersion().securityLevel != SecurityLevel::STRONGBOX) {
mDevice = std::move(dev);
break;
}
}
if (!mDevice) return;
auto& version = mDevice->halVersion();
LOG(INFO) << "Using " << version.keymasterName << " from " << version.authorName
<< " for encryption. Security level: " << toString(version.securityLevel)
<< ", HAL: " << mDevice->descriptor() << "/" << mDevice->instanceName() << std::endl;
}
bool Keymaster::generateKey(const km::AuthorizationSet& inParams, std::string* key) {
km::ErrorCode km_error;
auto hidlCb = [&](km::ErrorCode ret, const hidl_vec<uint8_t>& keyBlob,
const km::KeyCharacteristics& /*ignored*/) {
km_error = ret;
if (km_error != km::ErrorCode::OK) return;
if (key) key->assign(reinterpret_cast<const char*>(&keyBlob[0]), keyBlob.size());
};
auto error = mDevice->generateKey(inParams.hidl_data(), hidlCb);
if (!error.isOk()) {
LOG(ERROR) << "generate_key failed: " << error.description() << std::endl;
return false;
}
if (km_error != km::ErrorCode::OK) {
LOG(ERROR) << "generate_key failed, code " << int32_t(km_error) << std::endl;
return false;
}
return true;
}
km::ErrorCode Keymaster::exportKey(km::KeyFormat format, KeyBuffer& kmKey, const std::string& clientId,
const std::string& appData, std::string* key) {
auto kmKeyBlob = km::support::blob2hidlVec(std::string(kmKey.data(), kmKey.size()));
auto emptyAssign = NULL;
auto kmClientId = (clientId == "!") ? emptyAssign: km::support::blob2hidlVec(clientId);
auto kmAppData = (appData == "!") ? emptyAssign: km::support::blob2hidlVec(appData);
km::ErrorCode km_error;
auto hidlCb = [&](km::ErrorCode ret, const hidl_vec<uint8_t>& exportedKeyBlob) {
km_error = ret;
if (km_error != km::ErrorCode::OK) return;
if(key)
key->assign(reinterpret_cast<const char*>(&exportedKeyBlob[0]),
exportedKeyBlob.size());
};
auto error = mDevice->exportKey(format, kmKeyBlob, kmClientId, kmAppData, hidlCb);
if (!error.isOk()) {
LOG(ERROR) << "export_key failed: " << error.description();
return km::ErrorCode::UNKNOWN_ERROR;
}
if (km_error != km::ErrorCode::OK) {
LOG(ERROR) << "export_key failed, code " << int32_t(km_error);
return km_error;
}
return km::ErrorCode::OK;
}
bool Keymaster::deleteKey(const std::string& key) {
LOG(ERROR) << "not actually deleting key\n";
return true;
auto keyBlob = km::support::blob2hidlVec(key);
auto error = mDevice->deleteKey(keyBlob);
if (!error.isOk()) {
LOG(ERROR) << "delete_key failed: " << error.description();
return false;
}
if (error != km::ErrorCode::OK) {
LOG(ERROR) << "delete_key failed, code " << int32_t(km::ErrorCode(error));
return false;
}
return true;
}
bool Keymaster::upgradeKey(const std::string& oldKey, const km::AuthorizationSet& inParams,
std::string* newKey) {
auto oldKeyBlob = km::support::blob2hidlVec(oldKey);
km::ErrorCode km_error;
auto hidlCb = [&](km::ErrorCode ret, const hidl_vec<uint8_t>& upgradedKeyBlob) {
km_error = ret;
if (km_error != km::ErrorCode::OK) return;
if (newKey)
newKey->assign(reinterpret_cast<const char*>(&upgradedKeyBlob[0]),
upgradedKeyBlob.size());
};
auto error = mDevice->upgradeKey(oldKeyBlob, inParams.hidl_data(), hidlCb);
if (!error.isOk()) {
LOG(ERROR) << "upgrade_key failed: " << error.description() << std::endl;
return false;
}
if (km_error != km::ErrorCode::OK) {
LOG(ERROR) << "upgrade_key failed, code " << int32_t(km_error) << std::endl;
return false;
}
return true;
}
KeymasterOperation Keymaster::begin(km::KeyPurpose purpose, const std::string& key,
const km::AuthorizationSet& inParams,
const km::HardwareAuthToken& authToken,
km::AuthorizationSet* outParams) {
auto keyBlob = km::support::blob2hidlVec(key);
uint64_t mOpHandle;
km::ErrorCode km_error;
auto hidlCb = [&](km::ErrorCode ret, const hidl_vec<km::KeyParameter>& _outParams,
uint64_t operationHandle) {
km_error = ret;
if (km_error != km::ErrorCode::OK) return;
if (outParams) *outParams = _outParams;
mOpHandle = operationHandle;
};
auto error = mDevice->begin(purpose, keyBlob, inParams.hidl_data(), authToken, hidlCb);
if (!error.isOk()) {
LOG(ERROR) << "begin failed: " << error.description() << std::endl;
return KeymasterOperation(km::ErrorCode::UNKNOWN_ERROR);
}
if (km_error != km::ErrorCode::OK) {
LOG(ERROR) << "begin failed, code " << int32_t(km_error) << std::endl;
return KeymasterOperation(km_error);
}
return KeymasterOperation(mDevice.get(), mOpHandle);
}
bool Keymaster::isSecure() {
return mDevice->halVersion().securityLevel != km::SecurityLevel::SOFTWARE;
}
} // namespace vold
} // namespace android
using namespace ::android::vold;
/*
int keymaster_compatibility_cryptfs_scrypt() {
Keymaster dev;
if (!dev) {
LOG(ERROR) << "Failed to initiate keymaster session" << std::endl;
return -1;
}
return dev.isSecure();
}
*/
static bool write_string_to_buf(const std::string& towrite, uint8_t* buffer, uint32_t buffer_size,
uint32_t* out_size) {
if (!buffer || !out_size) {
LOG(ERROR) << "Missing target pointers" << std::endl;
return false;
}
*out_size = towrite.size();
if (buffer_size < towrite.size()) {
LOG(ERROR) << "Buffer too small " << buffer_size << " < " << towrite.size() << std::endl;
return false;
}
memset(buffer, '\0', buffer_size);
std::copy(towrite.begin(), towrite.end(), buffer);
return true;
}
static km::AuthorizationSet keyParams(uint32_t rsa_key_size, uint64_t rsa_exponent,
uint32_t ratelimit) {
return km::AuthorizationSetBuilder()
.RsaSigningKey(rsa_key_size, rsa_exponent)
.NoDigestOrPadding()
.Authorization(km::TAG_BLOB_USAGE_REQUIREMENTS, km::KeyBlobUsageRequirements::STANDALONE)
.Authorization(km::TAG_NO_AUTH_REQUIRED)
.Authorization(km::TAG_MIN_SECONDS_BETWEEN_OPS, ratelimit);
}
/*
int keymaster_create_key_for_cryptfs_scrypt(uint32_t rsa_key_size, uint64_t rsa_exponent,
uint32_t ratelimit, uint8_t* key_buffer,
uint32_t key_buffer_size, uint32_t* key_out_size) {
if (key_out_size) {
*key_out_size = 0;
}
Keymaster dev;
if (!dev) {
LOG(ERROR) << "Failed to initiate keymaster session" << std::endl;
return -1;
}
std::string key;
if (!dev.generateKey(keyParams(rsa_key_size, rsa_exponent, ratelimit), &key)) return -1;
if (!write_string_to_buf(key, key_buffer, key_buffer_size, key_out_size)) return -1;
return 0;
}
*/
int keymaster_upgrade_key_for_cryptfs_scrypt(uint32_t rsa_key_size, uint64_t rsa_exponent,
uint32_t ratelimit, const uint8_t* key_blob,
size_t key_blob_size, uint8_t* key_buffer,
uint32_t key_buffer_size, uint32_t* key_out_size) {
if (key_out_size) {
*key_out_size = 0;
}
Keymaster dev;
if (!dev) {
LOG(ERROR) << "Failed to initiate keymaster session" << std::endl;
return -1;
}
std::string old_key(reinterpret_cast<const char*>(key_blob), key_blob_size);
std::string new_key;
if (!dev.upgradeKey(old_key, keyParams(rsa_key_size, rsa_exponent, ratelimit), &new_key))
return -1;
if (!write_string_to_buf(new_key, key_buffer, key_buffer_size, key_out_size)) return -1;
return 0;
}
KeymasterSignResult keymaster_sign_object_for_cryptfs_scrypt(
const uint8_t* key_blob, size_t key_blob_size, uint32_t ratelimit, const uint8_t* object,
const size_t object_size, uint8_t** signature_buffer, size_t* signature_buffer_size) {
Keymaster dev;
if (!dev) {
LOG(ERROR) << "Failed to initiate keymaster session" << std::endl;
return KeymasterSignResult::error;
}
if (!key_blob || !object || !signature_buffer || !signature_buffer_size) {
LOG(ERROR) << __FILE__ << ":" << __LINE__ << ":Invalid argument" << std::endl;
return KeymasterSignResult::error;
}
km::AuthorizationSet outParams;
std::string key(reinterpret_cast<const char*>(key_blob), key_blob_size);
std::string input(reinterpret_cast<const char*>(object), object_size);
std::string output;
KeymasterOperation op;
auto paramBuilder = km::AuthorizationSetBuilder().NoDigestOrPadding();
while (true) {
op = dev.begin(km::KeyPurpose::SIGN, key, paramBuilder, km::HardwareAuthToken(), &outParams);
if (op.errorCode() == km::ErrorCode::KEY_RATE_LIMIT_EXCEEDED) {
sleep(ratelimit);
continue;
} else
break;
}
if (op.errorCode() == km::ErrorCode::KEY_REQUIRES_UPGRADE) {
LOG(ERROR) << "Keymaster key requires upgrade" << std::endl;
return KeymasterSignResult::upgrade;
}
if (op.errorCode() != km::ErrorCode::OK) {
LOG(ERROR) << "Error starting keymaster signature transaction: " << int32_t(op.errorCode()) << std::endl;
return KeymasterSignResult::error;
}
if (!op.updateCompletely(input, &output)) {
LOG(ERROR) << "Error sending data to keymaster signature transaction: "
<< uint32_t(op.errorCode()) << std::endl;
return KeymasterSignResult::error;
}
if (!op.finish(&output)) {
LOG(ERROR) << "Error finalizing keymaster signature transaction: "
<< int32_t(op.errorCode()) << std::endl;
return KeymasterSignResult::error;
}
*signature_buffer = reinterpret_cast<uint8_t*>(malloc(output.size()));
if (*signature_buffer == nullptr) {
LOG(ERROR) << "Error allocation buffer for keymaster signature" << std::endl;
return KeymasterSignResult::error;
}
*signature_buffer_size = output.size();
std::copy(output.data(), output.data() + output.size(), *signature_buffer);
return KeymasterSignResult::ok;
}
-162
View File
@@ -1,162 +0,0 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef ANDROID_TWRP_KEYMASTER_H
#define ANDROID_TWRP_KEYMASTER_H
#include "KeyBuffer.h"
#include <memory>
#include <string>
#include <utility>
#include <android-base/macros.h>
#include <keymasterV4_0/Keymaster.h>
#include <keymasterV4_0/authorization_set.h>
namespace android {
namespace vold {
namespace km = ::android::hardware::keymaster::V4_0;
using KmDevice = km::support::Keymaster;
// C++ wrappers to the Keymaster hidl interface.
// This is tailored to the needs of KeyStorage, but could be extended to be
// a more general interface.
// Wrapper for a Keymaster operation handle representing an
// ongoing Keymaster operation. Aborts the operation
// in the destructor if it is unfinished. Methods log failures
// to LOG(ERROR).
class KeymasterOperation {
public:
~KeymasterOperation();
// Is this instance valid? This is false if creation fails, and becomes
// false on finish or if an update fails.
explicit operator bool() { return mError == km::ErrorCode::OK; }
km::ErrorCode errorCode() { return mError; }
// Call "update" repeatedly until all of the input is consumed, and
// concatenate the output. Return true on success.
template <class TI, class TO>
bool updateCompletely(TI& input, TO* output) {
if (output) output->clear();
return updateCompletely(input.data(), input.size(), [&](const char* b, size_t n) {
if (output) std::copy(b, b + n, std::back_inserter(*output));
});
}
// Finish and write the output to this string, unless pointer is null.
bool finish(std::string* output);
// Move constructor
KeymasterOperation(KeymasterOperation&& rhs) { *this = std::move(rhs); }
// Construct an object in an error state for error returns
KeymasterOperation() : mDevice{nullptr}, mOpHandle{0}, mError{km::ErrorCode::UNKNOWN_ERROR} {}
// Move Assignment
KeymasterOperation& operator=(KeymasterOperation&& rhs) {
mDevice = rhs.mDevice;
rhs.mDevice = nullptr;
mOpHandle = rhs.mOpHandle;
rhs.mOpHandle = 0;
mError = rhs.mError;
rhs.mError = km::ErrorCode::UNKNOWN_ERROR;
return *this;
}
private:
KeymasterOperation(KmDevice* d, uint64_t h)
: mDevice{d}, mOpHandle{h}, mError{km::ErrorCode::OK} {}
KeymasterOperation(km::ErrorCode error) : mDevice{nullptr}, mOpHandle{0}, mError{error} {}
bool updateCompletely(const char* input, size_t inputLen,
const std::function<void(const char*, size_t)> consumer);
KmDevice* mDevice;
uint64_t mOpHandle;
km::ErrorCode mError;
DISALLOW_COPY_AND_ASSIGN(KeymasterOperation);
friend class Keymaster;
};
// Wrapper for a Keymaster device for methods that start a KeymasterOperation or are not
// part of one.
class Keymaster {
public:
Keymaster();
// false if we failed to open the keymaster device.
explicit operator bool() { return mDevice.get() != nullptr; }
// Generate a key in the keymaster from the given params.
bool generateKey(const km::AuthorizationSet& inParams, std::string* key);
// Export a key from keymaster.
km::ErrorCode exportKey(km::KeyFormat format, KeyBuffer& kmKey, const std::string& clientId,
const std::string& appData, std::string* key);
// If the keymaster supports it, permanently delete a key.
bool deleteKey(const std::string& key);
// Replace stored key blob in response to KM_ERROR_KEY_REQUIRES_UPGRADE.
bool upgradeKey(const std::string& oldKey, const km::AuthorizationSet& inParams,
std::string* newKey);
// Begin a new cryptographic operation, collecting output parameters if pointer is non-null
KeymasterOperation begin(km::KeyPurpose purpose, const std::string& key,
const km::AuthorizationSet& inParams,
const km::HardwareAuthToken& authToken,
km::AuthorizationSet* outParams);
bool isSecure();
private:
std::unique_ptr<KmDevice> mDevice;
DISALLOW_COPY_AND_ASSIGN(Keymaster);
static bool hmacKeyGenerated;
};
} // namespace vold
} // namespace android
// FIXME no longer needed now cryptfs is in C++.
/*
* The following functions provide C bindings to keymaster services
* needed by cryptfs scrypt. The compatibility check checks whether
* the keymaster implementation is considered secure, i.e., TEE backed.
* The create_key function generates an RSA key for signing.
* The sign_object function signes an object with the given keymaster
* key.
*/
/* Return values for keymaster_sign_object_for_cryptfs_scrypt */
enum class KeymasterSignResult {
ok = 0,
error = -1,
upgrade = -2,
};
//int keymaster_compatibility_cryptfs_scrypt();
/*int keymaster_create_key_for_cryptfs_scrypt(uint32_t rsa_key_size, uint64_t rsa_exponent,
uint32_t ratelimit, uint8_t* key_buffer,
uint32_t key_buffer_size, uint32_t* key_out_size);*/
int keymaster_upgrade_key_for_cryptfs_scrypt(uint32_t rsa_key_size, uint64_t rsa_exponent,
uint32_t ratelimit, const uint8_t* key_blob,
size_t key_blob_size, uint8_t* key_buffer,
uint32_t key_buffer_size, uint32_t* key_out_size);
KeymasterSignResult keymaster_sign_object_for_cryptfs_scrypt(
const uint8_t* key_blob, size_t key_blob_size, uint32_t ratelimit, const uint8_t* object,
const size_t object_size, uint8_t** signature_buffer, size_t* signature_buffer_size);
#endif
-271
View File
@@ -1,271 +0,0 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "KeyBuffer.h"
#include "MetadataCrypt.h"
#include <string>
#include <thread>
#include <vector>
#include <algorithm>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <linux/dm-ioctl.h>
//#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/unique_fd.h>
#include <cutils/fs.h>
//#include <fs_mgr.h>
//#include "EncryptInplace.h"
#include "KeyStorage4.h"
#include "KeyUtil.h"
//#include "secontext.h"
#include "Utils.h"
//#include "VoldUtil.h"
#include <iostream>
#define LOG(x) std::cout
#define PLOG(x) std::cout
#include <linux/fs.h>
#define DM_CRYPT_BUF_SIZE 4096
#define TABLE_LOAD_RETRIES 10
#define DEFAULT_KEY_TARGET_TYPE "default-key"
using android::vold::KeyBuffer;
static const std::string kDmNameUserdata = "userdata";
void get_blkdev_size(int fd, unsigned long* nr_sec) {
if ((ioctl(fd, BLKGETSIZE, nr_sec)) == -1) {
*nr_sec = 0;
}
}
static const char* kLookup = "0123456789abcdef";
android::status_t StrToHex(const KeyBuffer& str, KeyBuffer& hex) {
hex.clear();
for (size_t i = 0; i < str.size(); i++) {
hex.push_back(kLookup[(str.data()[i] & 0xF0) >> 4]);
hex.push_back(kLookup[str.data()[i] & 0x0F]);
}
return android::OK;
}
/*static bool mount_via_fs_mgr(const char* mount_point, const char* blk_device) {
// fs_mgr_do_mount runs fsck. Use setexeccon to run trusted
// partitions in the fsck domain.
if (setexeccon(secontextFsck())) {
PLOG(ERROR) << "Failed to setexeccon";
return false;
}
auto mount_rc = fs_mgr_do_mount(fstab_default, const_cast<char*>(mount_point),
const_cast<char*>(blk_device), nullptr);
if (setexeccon(nullptr)) {
PLOG(ERROR) << "Failed to clear setexeccon";
return false;
}
if (mount_rc != 0) {
LOG(ERROR) << "fs_mgr_do_mount failed with rc " << mount_rc;
return false;
}
LOG(DEBUG) << "Mounted " << mount_point;
return true;
}*/
static bool read_key(const std::string& key_dir, bool create_if_absent, KeyBuffer* key) {
/*if (!data_rec->key_dir) {
LOG(ERROR) << "Failed to get key_dir";
return false;
}
std::string key_dir = data_rec->key_dir;*/
auto dir = key_dir + "/key";
LOG(DEBUG) << "key_dir/key: " << dir << "\n";
/*if (fs_mkdirs(dir.c_str(), 0700)) {
PLOG(ERROR) << "Creating directories: " << dir;
return false;
}*/
auto temp = key_dir + "/tmp";
if (!android::vold::retrieveKey(create_if_absent, dir, temp, key)) return false;
return true;
}
static KeyBuffer default_key_params(const std::string& real_blkdev, const KeyBuffer& key) {
KeyBuffer hex_key;
if (/*android::vold::*/StrToHex(key, hex_key) != android::OK) {
LOG(ERROR) << "Failed to turn key to hex\n";
return KeyBuffer();
}
auto res = KeyBuffer() + "AES-256-XTS " + hex_key + " " + real_blkdev.c_str() + " 0";
return res;
}
static bool get_number_of_sectors(const std::string& real_blkdev, uint64_t *nr_sec) {
android::base::unique_fd dev_fd(TEMP_FAILURE_RETRY(open(
real_blkdev.c_str(), O_RDONLY | O_CLOEXEC, 0)));
if (dev_fd == -1) {
PLOG(ERROR) << "Unable to open " << real_blkdev << " to measure size\n";
return false;
}
unsigned long res;
// TODO: should use BLKGETSIZE64
get_blkdev_size(dev_fd.get(), &res);
if (res == 0) {
PLOG(ERROR) << "Unable to measure size of " << real_blkdev << "\n";
return false;
}
*nr_sec = res;
return true;
}
static struct dm_ioctl* dm_ioctl_init(char *buffer, size_t buffer_size,
const std::string& dm_name) {
if (buffer_size < sizeof(dm_ioctl)) {
LOG(ERROR) << "dm_ioctl buffer too small\n";
return nullptr;
}
memset(buffer, 0, buffer_size);
struct dm_ioctl* io = (struct dm_ioctl*) buffer;
io->data_size = buffer_size;
io->data_start = sizeof(struct dm_ioctl);
io->version[0] = 4;
io->version[1] = 0;
io->version[2] = 0;
io->flags = 0;
dm_name.copy(io->name, sizeof(io->name));
return io;
}
static bool create_crypto_blk_dev(const std::string& dm_name, uint64_t nr_sec,
const std::string& target_type, const KeyBuffer& crypt_params,
std::string* crypto_blkdev) {
PLOG(INFO) << "starting create_crypto_blk_dev\n";
android::base::unique_fd dm_fd(TEMP_FAILURE_RETRY(open(
"/dev/device-mapper", O_RDWR | O_CLOEXEC, 0)));
if (dm_fd == -1) {
PLOG(ERROR) << "Cannot open device-mapper\n";
return false;
}
alignas(struct dm_ioctl) char buffer[DM_CRYPT_BUF_SIZE];
auto io = dm_ioctl_init(buffer, sizeof(buffer), dm_name);
if (!io || ioctl(dm_fd.get(), DM_DEV_CREATE, io) != 0) {
PLOG(ERROR) << "Cannot create dm-crypt device " << dm_name << "\n";
return false;
}
// Get the device status, in particular, the name of its device file
io = dm_ioctl_init(buffer, sizeof(buffer), dm_name);
if (ioctl(dm_fd.get(), DM_DEV_STATUS, io) != 0) {
PLOG(ERROR) << "Cannot retrieve dm-crypt device status " << dm_name << "\n";
return false;
}
*crypto_blkdev = std::string() + "/dev/block/dm-" + std::to_string(
(io->dev & 0xff) | ((io->dev >> 12) & 0xfff00));
io = dm_ioctl_init(buffer, sizeof(buffer), dm_name);
size_t paramix = io->data_start + sizeof(struct dm_target_spec);
size_t nullix = paramix + crypt_params.size();
size_t endix = (nullix + 1 + 7) & 8; // Add room for \0 and align to 8 byte boundary
if (endix > sizeof(buffer)) {
LOG(ERROR) << "crypt_params too big for DM_CRYPT_BUF_SIZE\n";
return false;
}
io->target_count = 1;
auto tgt = (struct dm_target_spec *) (buffer + io->data_start);
tgt->status = 0;
tgt->sector_start = 0;
tgt->length = nr_sec;
target_type.copy(tgt->target_type, sizeof(tgt->target_type));
memcpy(buffer + paramix, crypt_params.data(),
std::min(crypt_params.size(), sizeof(buffer) - paramix));
buffer[nullix] = '\0';
tgt->next = endix;
for (int i = 0; ; i++) {
if (ioctl(dm_fd.get(), DM_TABLE_LOAD, io) == 0) {
break;
}
if (i+1 >= TABLE_LOAD_RETRIES) {
PLOG(ERROR) << "DM_TABLE_LOAD ioctl failed\n";
return false;
}
PLOG(INFO) << "DM_TABLE_LOAD ioctl failed, retrying\n";
usleep(500000);
}
// Resume this device to activate it
io = dm_ioctl_init(buffer, sizeof(buffer), dm_name);
if (ioctl(dm_fd.get(), DM_DEV_SUSPEND, io)) {
PLOG(ERROR) << "Cannot resume dm-crypt device " << dm_name << "\n";
return false;
}
return true;
}
bool e4crypt_mount_metadata_encrypted(const std::string& mount_point, bool needs_encrypt, const std::string& key_dir, const std::string& blk_device, std::string* crypto_blkdev) {
LOG(DEBUG) << "e4crypt_mount_metadata_encrypted: " << mount_point << " " << needs_encrypt << "\n";
/*auto encrypted_state = android::base::GetProperty("ro.crypto.state", "");
if (encrypted_state != "") {
LOG(DEBUG) << "e4crypt_enable_crypto got unexpected starting state: " << encrypted_state;
return false;
}
auto data_rec = fs_mgr_get_entry_for_mount_point(fstab_default, mount_point);
if (!data_rec) {
LOG(ERROR) << "Failed to get data_rec";
return false;
}*/
KeyBuffer key;
if (!read_key(key_dir, needs_encrypt, &key)) return false;
uint64_t nr_sec;
if (!get_number_of_sectors(blk_device, &nr_sec)) return false;
//std::string crypto_blkdev;
if (!create_crypto_blk_dev(kDmNameUserdata, nr_sec, DEFAULT_KEY_TARGET_TYPE,
default_key_params(blk_device, key), /*&*/crypto_blkdev))
return false;
// FIXME handle the corrupt case
/*if (needs_encrypt) {
LOG(INFO) << "Beginning inplace encryption, nr_sec: " << nr_sec;
off64_t size_already_done = 0;
auto rc =
cryptfs_enable_inplace(const_cast<char*>(crypto_blkdev.c_str()), data_rec->blk_device,
nr_sec, &size_already_done, nr_sec, 0, false);
if (rc != 0) {
LOG(ERROR) << "Inplace crypto failed with code: " << rc;
return false;
}
if (static_cast<uint64_t>(size_already_done) != nr_sec) {
LOG(ERROR) << "Inplace crypto only got up to sector: " << size_already_done;
return false;
}
LOG(INFO) << "Inplace encryption complete";
}
LOG(DEBUG) << "Mounting metadata-encrypted filesystem:" << mount_point;
mount_via_fs_mgr(data_rec->mount_point, crypto_blkdev.c_str());*/
LOG(DEBUG) << "crypto block device '" << *crypto_blkdev << "\n";
return true;
}
-25
View File
@@ -1,25 +0,0 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _METADATA_CRYPT_H
#define _METADATA_CRYPT_H
#include <string>
__BEGIN_DECLS
bool e4crypt_mount_metadata_encrypted(const std::string& mount_point, bool needs_encrypt, const std::string& key_dir, const std::string& blk_device, std::string* crypto_blkdev);
__END_DECLS
#endif
-50
View File
@@ -1,50 +0,0 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "ScryptParameters.h"
#include <stdlib.h>
#include <string.h>
bool parse_scrypt_parameters(const char* paramstr, int *Nf, int *rf, int *pf) {
int params[3];
char *token;
char *saveptr;
int i;
/*
* The token we're looking for should be three integers separated by
* colons (e.g., "12:8:1"). Scan the property to make sure it matches.
*/
for (i = 0, token = strtok_r(const_cast<char *>(paramstr), ":", &saveptr);
token != nullptr && i < 3;
i++, token = strtok_r(nullptr, ":", &saveptr)) {
char *endptr;
params[i] = strtol(token, &endptr, 10);
/*
* Check that there was a valid number and it's 8-bit.
*/
if ((*token == '\0') || (*endptr != '\0') || params[i] < 0 || params[i] > 255) {
return false;
}
}
if (token != nullptr) {
return false;
}
*Nf = params[0]; *rf = params[1]; *pf = params[2];
return true;
}
-32
View File
@@ -1,32 +0,0 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef ANDROID_VOLD_SCRYPT_PARAMETERS_H
#define ANDROID_VOLD_SCRYPT_PARAMETERS_H
#include <stdbool.h>
#include <sys/cdefs.h>
#define SCRYPT_PROP "ro.crypto.scrypt_params"
#define SCRYPT_DEFAULTS "15:3:1"
__BEGIN_DECLS
bool parse_scrypt_parameters(const char* paramstr, int *Nf, int *rf, int *pf);
__END_DECLS
#endif
-307
View File
@@ -1,307 +0,0 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "Utils.h"
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <fcntl.h>
#include <linux/fs.h>
#include <stdlib.h>
#include <sys/mount.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/statvfs.h>
#include <selinux/android.h>
using android::base::ReadFileToString;
using android::base::StringPrintf;
namespace android {
namespace vold {
static const char* kKeyPath = "/data/misc/vold";
status_t ForkExecvp(const std::vector<std::string>& args) {
return ForkExecvp(args, nullptr);
}
status_t ForkExecvp(const std::vector<std::string>& args, security_context_t context) {
size_t argc = args.size();
char** argv = (char**) calloc(argc, sizeof(char*));
for (size_t i = 0; i < argc; i++) {
argv[i] = (char*) args[i].c_str();
if (i == 0) {
LOG(VERBOSE) << args[i];
} else {
LOG(VERBOSE) << " " << args[i];
}
}
if (setexeccon(context)) {
LOG(ERROR) << "Failed to setexeccon" << std::endl;
abort();
}
abort();
status_t res = 1;//android_fork_execvp(argc, argv, NULL, false, true);
if (setexeccon(nullptr)) {
LOG(ERROR) << "Failed to setexeccon" << std::endl;
abort();
}
free(argv);
return res;
}
status_t ForkExecvp(const std::vector<std::string>& args,
std::vector<std::string>& output) {
return ForkExecvp(args, output, nullptr);
}
status_t ForkExecvp(const std::vector<std::string>& args,
std::vector<std::string>& output, security_context_t context) {
std::string cmd;
for (size_t i = 0; i < args.size(); i++) {
cmd += args[i] + " ";
if (i == 0) {
LOG(VERBOSE) << args[i];
} else {
LOG(VERBOSE) << " " << args[i];
}
}
output.clear();
if (setexeccon(context)) {
LOG(ERROR) << "Failed to setexeccon" << std::endl;
abort();
}
FILE* fp = popen(cmd.c_str(), "r");
if (setexeccon(nullptr)) {
LOG(ERROR) << "Failed to setexeccon" << std::endl;
abort();
}
if (!fp) {
PLOG(ERROR) << "Failed to popen " << cmd << std::endl;
return -errno;
}
char line[1024];
while (fgets(line, sizeof(line), fp) != nullptr) {
LOG(VERBOSE) << line;
output.push_back(std::string(line));
}
if (pclose(fp) != 0) {
PLOG(ERROR) << "Failed to pclose " << cmd << std::endl;
return -errno;
}
return OK;
}
pid_t ForkExecvpAsync(const std::vector<std::string>& args) {
size_t argc = args.size();
char** argv = (char**) calloc(argc + 1, sizeof(char*));
for (size_t i = 0; i < argc; i++) {
argv[i] = (char*) args[i].c_str();
if (i == 0) {
LOG(VERBOSE) << args[i];
} else {
LOG(VERBOSE) << " " << args[i];
}
}
pid_t pid = fork();
if (pid == 0) {
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
if (execvp(argv[0], argv)) {
PLOG(ERROR) << "Failed to exec" << std::endl;
}
_exit(1);
}
if (pid == -1) {
PLOG(ERROR) << "Failed to exec" << std::endl;
}
free(argv);
return pid;
}
status_t ReadRandomBytes(size_t bytes, std::string& out) {
out.resize(bytes);
return ReadRandomBytes(bytes, &out[0]);
}
status_t ReadRandomBytes(size_t bytes, char* buf) {
int fd = TEMP_FAILURE_RETRY(open("/dev/urandom", O_RDONLY | O_CLOEXEC | O_NOFOLLOW));
if (fd == -1) {
return -errno;
}
size_t n;
while ((n = TEMP_FAILURE_RETRY(read(fd, &buf[0], bytes))) > 0) {
bytes -= n;
buf += n;
}
close(fd);
if (bytes == 0) {
return OK;
} else {
return -EIO;
}
}
status_t HexToStr(const std::string& hex, std::string& str) {
str.clear();
bool even = true;
char cur = 0;
for (size_t i = 0; i < hex.size(); i++) {
int val = 0;
switch (hex[i]) {
case ' ': case '-': case ':': continue;
case 'f': case 'F': val = 15; break;
case 'e': case 'E': val = 14; break;
case 'd': case 'D': val = 13; break;
case 'c': case 'C': val = 12; break;
case 'b': case 'B': val = 11; break;
case 'a': case 'A': val = 10; break;
case '9': val = 9; break;
case '8': val = 8; break;
case '7': val = 7; break;
case '6': val = 6; break;
case '5': val = 5; break;
case '4': val = 4; break;
case '3': val = 3; break;
case '2': val = 2; break;
case '1': val = 1; break;
case '0': val = 0; break;
default: return -EINVAL;
}
if (even) {
cur = val << 4;
} else {
cur += val;
str.push_back(cur);
cur = 0;
}
even = !even;
}
return even ? OK : -EINVAL;
}
static bool isValidFilename(const std::string& name) {
if (name.empty() || (name == ".") || (name == "..")
|| (name.find('/') != std::string::npos)) {
return false;
} else {
return true;
}
}
std::string BuildKeyPath(const std::string& partGuid) {
return StringPrintf("%s/expand_%s.key", kKeyPath, partGuid.c_str());
}
std::string BuildDataSystemLegacyPath(userid_t userId) {
return StringPrintf("%s/system/users/%u", BuildDataPath(nullptr).c_str(), userId);
}
std::string BuildDataSystemCePath(userid_t userId) {
return StringPrintf("%s/system_ce/%u", BuildDataPath(nullptr).c_str(), userId);
}
std::string BuildDataSystemDePath(userid_t userId) {
return StringPrintf("%s/system_de/%u", BuildDataPath(nullptr).c_str(), userId);
}
std::string BuildDataMiscLegacyPath(userid_t userId) {
return StringPrintf("%s/misc/user/%u", BuildDataPath(nullptr).c_str(), userId);
}
std::string BuildDataMiscCePath(userid_t userId) {
return StringPrintf("%s/misc_ce/%u", BuildDataPath(nullptr).c_str(), userId);
}
std::string BuildDataMiscDePath(userid_t userId) {
return StringPrintf("%s/misc_de/%u", BuildDataPath(nullptr).c_str(), userId);
}
std::string BuildDataVendorCePath(userid_t userId) {
return StringPrintf("%s/vendor_ce/%u", BuildDataPath(nullptr).c_str(), userId);
}
std::string BuildDataVendorDePath(userid_t userId) {
return StringPrintf("%s/vendor_de/%u", BuildDataPath(nullptr).c_str(), userId);
}
// Keep in sync with installd (frameworks/native/cmds/installd/utils.h)
std::string BuildDataProfilesDePath(userid_t userId) {
return StringPrintf("%s/misc/profiles/cur/%u", BuildDataPath(nullptr).c_str(), userId);
}
std::string BuildDataProfilesForeignDexDePath(userid_t userId) {
std::string profiles_path = BuildDataProfilesDePath(userId);
return StringPrintf("%s/foreign-dex", profiles_path.c_str());
}
std::string BuildDataPath(const char* volumeUuid) {
// TODO: unify with installd path generation logic
if (volumeUuid == nullptr) {
return "/data";
} else {
CHECK(isValidFilename(volumeUuid));
return StringPrintf("/mnt/expand/%s", volumeUuid);
}
}
std::string BuildDataMediaCePath(const char* volumeUuid, userid_t userId) {
// TODO: unify with installd path generation logic
std::string data(BuildDataPath(volumeUuid));
return StringPrintf("%s/media/%u", data.c_str(), userId);
}
std::string BuildDataUserCePath(const char* volumeUuid, userid_t userId) {
// TODO: unify with installd path generation logic
std::string data(BuildDataPath(volumeUuid));
if (volumeUuid == nullptr) {
if (userId == 0) {
return StringPrintf("%s/data", data.c_str());
} else {
return StringPrintf("%s/user/%u", data.c_str(), userId);
}
} else {
return StringPrintf("%s/user/%u", data.c_str(), userId);
}
}
std::string BuildDataUserDePath(const char* volumeUuid, userid_t userId) {
// TODO: unify with installd path generation logic
std::string data(BuildDataPath(volumeUuid));
return StringPrintf("%s/user_de/%u", data.c_str(), userId);
}
} // namespace vold
} // namespace android
-76
View File
@@ -1,76 +0,0 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TWRP_VOLD_UTILS_H
#define TWRP_VOLD_UTILS_H
#include <utils/Errors.h>
#include <cutils/multiuser.h>
#include <selinux/selinux.h>
#include <vector>
#include <string>
// DISALLOW_COPY_AND_ASSIGN disallows the copy and operator= functions. It goes in the private:
// declarations in a class.
#if !defined(DISALLOW_COPY_AND_ASSIGN)
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
TypeName(const TypeName&) = delete; \
void operator=(const TypeName&) = delete
#endif
namespace android {
namespace vold {
/* Returns either WEXITSTATUS() status, or a negative errno */
status_t ForkExecvp(const std::vector<std::string>& args);
status_t ForkExecvp(const std::vector<std::string>& args, security_context_t context);
status_t ForkExecvp(const std::vector<std::string>& args,
std::vector<std::string>& output);
status_t ForkExecvp(const std::vector<std::string>& args,
std::vector<std::string>& output, security_context_t context);
pid_t ForkExecvpAsync(const std::vector<std::string>& args);
status_t ReadRandomBytes(size_t bytes, std::string& out);
status_t ReadRandomBytes(size_t bytes, char* buffer);
/* Converts hex string to raw bytes, ignoring [ :-] */
status_t HexToStr(const std::string& hex, std::string& str);
std::string BuildKeyPath(const std::string& partGuid);
std::string BuildDataSystemLegacyPath(userid_t userid);
std::string BuildDataSystemCePath(userid_t userid);
std::string BuildDataSystemDePath(userid_t userid);
std::string BuildDataMiscLegacyPath(userid_t userid);
std::string BuildDataMiscCePath(userid_t userid);
std::string BuildDataMiscDePath(userid_t userid);
std::string BuildDataProfilesDePath(userid_t userid);
std::string BuildDataProfilesForeignDexDePath(userid_t userid);
std::string BuildDataVendorCePath(userid_t userid);
std::string BuildDataVendorDePath(userid_t userid);
std::string BuildDataPath(const char* volumeUuid);
std::string BuildDataMediaCePath(const char* volumeUuid, userid_t userid);
std::string BuildDataUserCePath(const char* volumeUuid, userid_t userid);
std::string BuildDataUserDePath(const char* volumeUuid, userid_t userid);
} // namespace vold
} // namespace android
#endif
-128
View File
@@ -1,128 +0,0 @@
/*
* Copyright (C) 2017 Team Win Recovery Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* To the best of my knowledge there is no native implementation for
* Weaver so I made this by looking at the IWeaver.h file that gets
* compiled by the build system. I took the information from this header
* file and looked at keymaster source to get an idea of the proper way
* to write the functions.
*/
#include "Weaver1.h"
//#include <android-base/logging.h>
//#include <keystore/keymaster_tags.h>
//#include <keystore/authorization_set.h>
//#include <keystore/keystore_hidl_support.h>
#include <android/hardware/weaver/1.0/IWeaver.h>
#include <iostream>
#define ERROR 1
#define LOG(x) std::cout
using namespace android::hardware::weaver;
using android::hardware::hidl_string;
using ::android::hardware::weaver::V1_0::IWeaver;
using ::android::hardware::weaver::V1_0::WeaverConfig;
using ::android::hardware::weaver::V1_0::WeaverReadStatus;
using ::android::hardware::weaver::V1_0::WeaverReadResponse;
using ::android::hardware::weaver::V1_0::WeaverStatus;
using ::android::hardware::Return;
using ::android::sp;
namespace android {
namespace vold {
Weaver::Weaver() {
mDevice = ::android::hardware::weaver::V1_0::IWeaver::getService();
GottenConfig = false;
}
bool Weaver::GetConfig() {
if (GottenConfig)
return true;
WeaverStatus status;
WeaverConfig cfg;
bool callbackCalled = false;
auto ret = mDevice->getConfig([&](WeaverStatus s, WeaverConfig c) {
callbackCalled = true;
status = s;
cfg = c;
});
if (ret.isOk() && callbackCalled && status == WeaverStatus::OK) {
config = cfg;
GottenConfig = true;
return true;
}
return false;
}
bool Weaver::GetSlots(uint32_t* slots) {
if (!GetConfig())
return false;
*slots = config.slots;
return true;
}
bool Weaver::GetKeySize(uint32_t* keySize) {
if (!GetConfig())
return false;
*keySize = config.keySize;
return true;
}
bool Weaver::GetValueSize(uint32_t* valueSize) {
if (!GetConfig())
return false;
*valueSize = config.valueSize;
return true;
}
// TODO: we should return more information about the status including time delays before the next retry
bool Weaver::WeaverVerify(const uint32_t slot, const void* weaver_key, std::vector<uint8_t>* payload) {
bool callbackCalled = false;
WeaverReadStatus status;
std::vector<uint8_t> readValue;
uint32_t timeout;
uint32_t keySize;
if (!GetKeySize(&keySize))
return false;
std::vector<uint8_t> key;
key.resize(keySize);
uint32_t index = 0;
unsigned char* ptr = (unsigned char*)weaver_key;
for (index = 0; index < keySize; index++) {
key[index] = *ptr;
ptr++;
}
const auto readRet = mDevice->read(slot, key, [&](WeaverReadStatus s, WeaverReadResponse r) {
callbackCalled = true;
status = s;
readValue = r.value;
timeout = r.timeout;
});
if (readRet.isOk() && callbackCalled && status == WeaverReadStatus::OK && timeout == 0) {
*payload = readValue;
return true;
}
return false;
}
} // namespace vold
} // namespace android
-64
View File
@@ -1,64 +0,0 @@
/*
* Copyright (C) 2017 Team Win Recovery Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* To the best of my knowledge there is no native implementation for
* Weaver so I made this by looking at the IWeaver.h file that gets
* compiled by the build system. I took the information from this header
* file and looked at keymaster source to get an idea of the proper way
* to write the functions.
*/
#ifndef TWRP_WEAVER_H
#define TWRP_WEAVER_H
#include <memory>
#include <string>
#include <utility>
#include <android/hardware/weaver/1.0/IWeaver.h>
#include "Utils.h"
namespace android {
namespace vold {
using ::android::hardware::weaver::V1_0::IWeaver;
// Wrapper for a Weaver device
class Weaver {
public:
Weaver();
// false if we failed to open the weaver device.
explicit operator bool() { return mDevice.get() != nullptr; }
bool GetSlots(uint32_t* slots);
bool GetKeySize(uint32_t* keySize);
bool GetValueSize(uint32_t* valueSize);
// TODO: we should return more information about the status including time delays before the next retry
bool WeaverVerify(const uint32_t slot, const void* weaver_key, std::vector<uint8_t>* payload);
private:
sp<hardware::weaver::V1_0::IWeaver> mDevice;
hardware::weaver::V1_0::WeaverConfig config;
bool GottenConfig;
bool GetConfig();
DISALLOW_COPY_AND_ASSIGN(Weaver);
};
} // namespace vold
} // namespace android
#endif
-41
View File
@@ -1,41 +0,0 @@
/*
* Copyright (C) 2016 Team Win Recovery Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ext4crypt_tar.h"
#define EXT4_KEY_DESCRIPTOR_SIZE 8
#define EXT4_KEY_DESCRIPTOR_SIZE_HEX 17
int main(int argc, char *argv[]) {
bool ret = false;
if (argc != 2) {
printf("Must specify a path\n");
return -1;
} else {
ext4_encryption_policy eep;
if (e4crypt_policy_get_struct(argv[1], &eep, sizeof(eep))) {
char policy_hex[EXT4_KEY_DESCRIPTOR_SIZE_HEX];
policy_to_hex(eep.master_key_descriptor, policy_hex);
printf("%s\n", policy_hex);
} else {
printf("No policy set\n");
}
}
return 0;
}
-207
View File
@@ -1,207 +0,0 @@
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* TWRP NOTE: Kanged from system/extras/ext4_utils/ext4_crypt.cpp
* because policy_to_hex, e4crypt_policy_set, and e4crypt_policy_get
* are not exposed to be used. There was also a bug in e4crypt_policy_get
* that may or may not be fixed in the user's local repo:
* https://android.googlesource.com/platform/system/extras/+/30b93dd5715abcabd621235733733c0503f9c552
*/
#include "ext4_crypt.h"
#include "ext4crypt_tar.h"
#include <dirent.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <asm/ioctl.h>
#include <sys/syscall.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <cutils/properties.h>
#define XATTR_NAME_ENCRYPTION_POLICY "encryption.policy"
#define EXT4_KEYREF_DELIMITER ((char)'.')
#define EXT4_ENCRYPTION_MODE_AES_256_XTS 1
#define EXT4_ENCRYPTION_MODE_AES_256_CTS 4
#define EXT4_ENCRYPTION_MODE_AES_256_HEH 126
#define EXT4_ENCRYPTION_MODE_PRIVATE 127
static int encryption_mode = EXT4_ENCRYPTION_MODE_PRIVATE;
#define HEX_LOOKUP "0123456789abcdef"
extern "C" void policy_to_hex(const char* policy, char* hex) {
for (size_t i = 0, j = 0; i < EXT4_KEY_DESCRIPTOR_SIZE; i++) {
hex[j++] = HEX_LOOKUP[(policy[i] & 0xF0) >> 4];
hex[j++] = HEX_LOOKUP[policy[i] & 0x0F];
}
hex[EXT4_KEY_DESCRIPTOR_SIZE_HEX - 1] = '\0';
}
extern "C" bool e4crypt_policy_set(const char *directory, const char *policy,
size_t policy_length, int contents_encryption_mode) {
if (contents_encryption_mode == 0)
contents_encryption_mode = encryption_mode;
if (policy_length != EXT4_KEY_DESCRIPTOR_SIZE) {
printf("policy wrong length\n");
LOG(ERROR) << "Policy wrong length: " << policy_length;
return false;
}
int fd = open(directory, O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC);
if (fd == -1) {
printf("failed to open %s\n", directory);
PLOG(ERROR) << "Failed to open directory " << directory;
return false;
}
ext4_encryption_policy eep;
eep.version = 0;
eep.contents_encryption_mode = contents_encryption_mode;
eep.filenames_encryption_mode = EXT4_ENCRYPTION_MODE_AES_256_CTS;
eep.flags = 0;
memcpy(eep.master_key_descriptor, policy, EXT4_KEY_DESCRIPTOR_SIZE);
if (ioctl(fd, EXT4_IOC_SET_ENCRYPTION_POLICY, &eep)) {
printf("failed to set policy for '%s' '%s'\n", directory, policy);
PLOG(ERROR) << "Failed to set encryption policy for " << directory;
close(fd);
return false;
}
close(fd);
char policy_hex[EXT4_KEY_DESCRIPTOR_SIZE_HEX];
policy_to_hex(policy, policy_hex);
LOG(INFO) << "Policy for " << directory << " set to " << policy_hex;
return true;
}
extern "C" bool e4crypt_policy_get(const char *directory, char *policy,
size_t policy_length, int contents_encryption_mode) {
if (contents_encryption_mode == 0)
contents_encryption_mode = encryption_mode;
if (policy_length != EXT4_KEY_DESCRIPTOR_SIZE) {
LOG(ERROR) << "Policy wrong length: " << policy_length;
return false;
}
int fd = open(directory, O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC);
if (fd == -1) {
PLOG(ERROR) << "Failed to open directory " << directory;
return false;
}
ext4_encryption_policy eep;
memset(&eep, 0, sizeof(ext4_encryption_policy));
if (ioctl(fd, EXT4_IOC_GET_ENCRYPTION_POLICY, &eep) != 0) {
PLOG(ERROR) << "Failed to get encryption policy for " << directory;
close(fd);
return false;
}
close(fd);
if ((eep.version != 0)
|| (eep.contents_encryption_mode != contents_encryption_mode)
|| (eep.filenames_encryption_mode != EXT4_ENCRYPTION_MODE_AES_256_CTS)
|| (eep.flags != 0)) {
LOG(ERROR) << "Failed to find matching encryption policy for " << directory;
return false;
}
memcpy(policy, eep.master_key_descriptor, EXT4_KEY_DESCRIPTOR_SIZE);
return true;
}
extern "C" void e4crypt_policy_fill_default_struct(ext4_encryption_policy *eep) {
eep->version = 0;
eep->contents_encryption_mode = encryption_mode;
eep->filenames_encryption_mode = EXT4_ENCRYPTION_MODE_AES_256_CTS;
eep->flags = 0;
memset((void*)&eep->master_key_descriptor[0], 0, EXT4_KEY_DESCRIPTOR_SIZE);
}
extern "C" bool e4crypt_policy_set_struct(const char *directory, const ext4_encryption_policy *eep) {
int fd = open(directory, O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC);
if (fd == -1) {
printf("failed to open %s\n", directory);
PLOG(ERROR) << "Failed to open directory " << directory;
return false;
}
if (ioctl(fd, EXT4_IOC_SET_ENCRYPTION_POLICY, eep)) {
printf("failed to set policy for '%s'\n", directory);
PLOG(ERROR) << "Failed to set encryption policy for " << directory;
close(fd);
return false;
}
close(fd);
return true;
}
extern "C" bool e4crypt_policy_get_struct(const char *directory, ext4_encryption_policy *eep) {
int fd = open(directory, O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC);
if (fd == -1) {
printf("Failed to open '%s'\n", directory);
PLOG(ERROR) << "Failed to open directory " << directory;
return false;
}
memset(eep, 0, sizeof(ext4_encryption_policy));
if (ioctl(fd, EXT4_IOC_GET_ENCRYPTION_POLICY, eep) != 0) {
PLOG(ERROR) << "Failed to get encryption policy for " << directory;
close(fd);
return false;
}
close(fd);
return true;
}
extern "C" bool e4crypt_set_mode() {
const char* mode_file = "/data/unencrypted/mode";
struct stat st;
if (stat(mode_file, &st) != 0 || st.st_size <= 0) {
printf("Invalid encryption mode file %s\n", mode_file);
return false;
}
size_t mode_size = st.st_size;
char contents_encryption_mode[mode_size + 1];
memset((void*)contents_encryption_mode, 0, mode_size + 1);
int fd = open(mode_file, O_RDONLY);
if (fd < 0) {
printf("error opening '%s': %s\n", mode_file, strerror(errno));
return false;
}
if (read(fd, contents_encryption_mode, mode_size) != mode_size) {
printf("read error on '%s': %s\n", mode_file, strerror(errno));
close(fd);
return false;
}
close(fd);
if (!strcmp(contents_encryption_mode, "software")) {
encryption_mode = EXT4_ENCRYPTION_MODE_AES_256_XTS;
} else if (!strcmp(contents_encryption_mode, "ice")) {
encryption_mode = EXT4_ENCRYPTION_MODE_PRIVATE;
} else {
printf("Invalid encryption mode '%s'\n", contents_encryption_mode);
return false;
}
printf("set encryption mode to %i\n", encryption_mode);
return true;
}
-39
View File
@@ -1,39 +0,0 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _EXT4_CRYPT_H_
#define _EXT4_CRYPT_H_
#include <sys/cdefs.h>
#include <stdbool.h>
#include <cutils/multiuser.h>
__BEGIN_DECLS
bool e4crypt_is_native();
int e4crypt_policy_ensure(const char *directory, const char *policy,
size_t policy_length,
const char *contents_encryption_mode,
const char *filenames_encryption_mode);
static const char* e4crypt_unencrypted_folder = "/unencrypted";
static const char* e4crypt_key_ref = "/unencrypted/ref";
static const char* e4crypt_key_mode = "/unencrypted/mode";
__END_DECLS
#endif // _EXT4_CRYPT_H_
-58
View File
@@ -1,58 +0,0 @@
/*
* Copyright (C) 2016 Team Win Recovery Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __EXT4CRYPT_TAR_H
#define __EXT4CRYPT_TAR_H
#include <sys/cdefs.h>
#include <stdbool.h>
#include <cutils/multiuser.h>
// ext4enc:TODO Include structure from somewhere sensible
// MUST be in sync with ext4_crypto.c in kernel
#define EXT4_KEY_DESCRIPTOR_SIZE 8
#define EXT4_KEY_DESCRIPTOR_SIZE_HEX 17
// ext4enc:TODO Get value from somewhere sensible
#define EXT4_IOC_SET_ENCRYPTION_POLICY _IOR('f', 19, struct ext4_encryption_policy)
#define EXT4_IOC_GET_ENCRYPTION_POLICY _IOW('f', 21, struct ext4_encryption_policy)
__BEGIN_DECLS
struct ext4_encryption_policy {
char version;
char contents_encryption_mode;
char filenames_encryption_mode;
char flags;
char master_key_descriptor[EXT4_KEY_DESCRIPTOR_SIZE];
} __attribute__((__packed__));
bool lookup_ref_key(const char* policy, char* policy_type);
bool lookup_ref_tar(const char* policy_type, char* policy);
void policy_to_hex(const char* policy, char* hex);
bool e4crypt_policy_set(const char *directory, const char *policy,
size_t policy_length, int contents_encryption_mode);
bool e4crypt_policy_get(const char *directory, char *policy,
size_t policy_length, int contents_encryption_mode);
void e4crypt_policy_fill_default_struct(struct ext4_encryption_policy *eep);
bool e4crypt_policy_set_struct(const char *directory, const struct ext4_encryption_policy *eep);
bool e4crypt_policy_get_struct(const char *directory, struct ext4_encryption_policy *eep);
bool e4crypt_set_mode();
__END_DECLS
#endif
-107
View File
@@ -1,107 +0,0 @@
/*
Copyright 2018 bigbiff/Dees_Troy TeamWin
This file is part of TWRP/TeamWin Recovery Project.
TWRP is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
TWRP is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with TWRP. If not, see <http://www.gnu.org/licenses/>.
*/
/* The keystore refuses to allow the root user to supply auth tokens, so
* we write the auth token to a file in TWRP and run a separate service
* (this) that runs as the system user to add the auth token. TWRP waits
* for /auth_token to be deleted and also looks for /auth_error to check
* for errors. TWRP will error out after a while if /auth_token does not
* get deleted. */
#include <stdio.h>
#include <string>
#ifdef USE_SECURITY_NAMESPACE
#include <android/security/IKeystoreService.h>
#else
#include <keystore/IKeystoreService.h>
#include <keystore/authorization_set.h>
#endif
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <keystore/keystore.h>
#ifndef LOG_TAG
#define LOG_TAG "keystore_auth"
#endif
using namespace android;
void create_error_file() {
FILE* error_file = fopen("/auth_error", "wb");
if (error_file == NULL) {
printf("Failed to open /auth_error\n");
ALOGE("Failed to open /auth_error\n");
return;
}
fwrite("1", 1, 1, error_file);
fclose(error_file);
unlink("/auth_token");
}
int main() {
unlink("/auth_error");
FILE* auth_file = fopen("/auth_token", "rb");
if (auth_file == NULL) {
printf("Failed to open /auth_token\n");
ALOGE("Failed to open /auth_token\n");
create_error_file();
return -1;
}
// Get the file size
fseek(auth_file, 0, SEEK_END);
int size = ftell(auth_file);
fseek(auth_file, 0, SEEK_SET);
uint8_t auth_token[size];
fread(auth_token , sizeof(uint8_t), size, auth_file);
fclose(auth_file);
// First get the keystore service
sp<IServiceManager> sm = defaultServiceManager();
sp<IBinder> binder = sm->getService(String16("android.security.keystore"));
#ifdef USE_SECURITY_NAMESPACE
sp<security::IKeystoreService> service = interface_cast<security::IKeystoreService>(binder);
#else
sp<IKeystoreService> service = interface_cast<IKeystoreService>(binder);
#endif
if (service == NULL) {
printf("error: could not connect to keystore service\n");
ALOGE("error: could not connect to keystore service\n");
create_error_file();
return -2;
}
#ifdef USE_SECURITY_NAMESPACE
std::vector<uint8_t> auth_token_vector(&auth_token[0], (&auth_token[0]) + size);
int result = 0;
auto binder_result = service->addAuthToken(auth_token_vector, &result);
if (!binder_result.isOk() || !keystore::KeyStoreServiceReturnCode(result).isOk()) {
#else
::keystore::KeyStoreServiceReturnCode auth_result = service->addAuthToken(auth_token, size);
if (!auth_result.isOk()) {
#endif
// The keystore checks the uid of the calling process and will return a permission denied on this operation for user 0
printf("keystore error adding auth token\n");
ALOGE("keystore error adding auth token\n");
create_error_file();
return -3;
}
printf("successfully added auth token to keystore\n");
ALOGD("successfully added auth token to keystore\n");
unlink("/auth_token");
return 0;
}
-36
View File
@@ -1,36 +0,0 @@
/*
* Copyright (C) 2016 Team Win Recovery Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "Decrypt.h"
int main(int argc, char *argv[]) {
bool ret = false;
if (argc < 2) {
Decrypt_DE();
ret = Decrypt_User(0, "0000");
} else if (argc < 3) {
Decrypt_DE();
ret = Decrypt_User(0, argv[1]);
} else {
ret = Decrypt_User(atoi(argv[1]), argv[2]);
}
if (!ret)
printf("Failed to decrypt\n");
return 0;
}
+2
View File
@@ -14,6 +14,8 @@ ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 23; echo $$?),0)
LOCAL_CPPFLAGS := -std=c++11
endif
LOCAL_C_INCLUDES += $(commands_TWRP_local_path)/crypto/fscrypt
ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 26; echo $$?),0)
#8.0 or higher
LOCAL_C_INCLUDES += external/boringssl/src/include
+2 -7
View File
@@ -60,12 +60,7 @@
//#include "f2fs_sparseblock.h"
//#include "EncryptInplace.h"
//#include "Process.h"
#if TW_KEYMASTER_MAX_API == 3
#include "../ext4crypt/Keymaster3.h"
#endif
#if TW_KEYMASTER_MAX_API == 4
#include "../ext4crypt/Keymaster4.h"
#endif
#include "Keymaster.h"
#if TW_KEYMASTER_MAX_API == 0
#include <hardware/keymaster.h>
#else // so far, all trees that have keymaster >= 1 have keymaster 1 support
@@ -879,7 +874,7 @@ static int get_crypt_ftr_and_key(struct crypt_mnt_ftr *crypt_ftr)
return -1;
}
if (fname[0] != '/') {
SLOGE("Unexpected value for crypto key location\n");
SLOGE("fde::get_crypt_ftr_and_key::Unexpected value for crypto key location: %s\n", fname);
return -1;
}
if ( (fd = open(fname, O_RDWR|O_CLOEXEC)) < 0) {
+19 -18
View File
@@ -5,16 +5,17 @@ include $(CLEAR_VARS)
LOCAL_MODULE := libtwrpfscrypt
LOCAL_MODULE_TAGS := optional
LOCAL_CFLAGS := -Wno-unused-variable -Wno-sign-compare -Wno-unused-parameter -Wno-comment -Wno-missing-field-initializers \
-DHAVE_LIBKEYUTILS -std=gnu++2a -Wno-macro-redefined -Wno-unused-function
LOCAL_SRC_FILES := Decrypt.cpp ScryptParameters.cpp Utils.cpp HashPassword.cpp \
FsCrypt.cpp KeyUtil.cpp Keymaster.cpp KeyStorage.cpp MetadataCrypt.cpp KeyBuffer.cpp \
Process.cpp EncryptInplace.cpp Weaver1.cpp fscrypt_policy.cpp
-DHAVE_LIBKEYUTILS -std=gnu++2a -Wno-macro-redefined -Wno-unused-function -fpic
LOCAL_SRC_FILES := FsCrypt.cpp Decrypt.cpp ScryptParameters.cpp fscrypt_policy.cpp Utils.cpp HashPassword.cpp \
KeyUtil.cpp Keymaster.cpp KeyStorage.cpp MetadataCrypt.cpp KeyBuffer.cpp \
Process.cpp EncryptInplace.cpp Weaver1.cpp cryptfs.cpp Checkpoint.cpp CryptoType.cpp VoldUtil.cpp
LOCAL_SHARED_LIBRARIES := libselinux libc libc++ libext4_utils libbase libcrypto libcutils \
libkeymaster_messages libhardware libprotobuf-cpp-lite libfscrypt android.hardware.confirmationui@1.0 \
android.hardware.keymaster@3.0 libkeystore_binder libhidlbase libutils libbinder android.hardware.gatekeeper@1.0 \
libfs_mgr android.hardware.keymaster@4.0 libkeymaster4support libf2fs_sparseblock libkeystore_parcelables \
libkeystore_aidl android.hardware.weaver@1.0 libkeyutils liblog libhwbinder libchrome
LOCAL_STATIC_LIBRARIES := libscrypt_static
libfs_mgr android.hardware.keymaster@4.0 android.hardware.keymaster@4.1 libkeymaster4support libkeymaster4_1support \
libf2fs_sparseblock libkeystore_parcelables libkeystore_aidl android.hardware.weaver@1.0 libkeyutils liblog libhwbinder \
libchrome android.hardware.boot@1.0 libbootloader_message
LOCAL_STATIC_LIBRARIES := libscrypt_static libvold_binder libc++fs
LOCAL_C_INCLUDES := system/extras/ext4_utils \
system/extras/ext4_utils/include/ext4_utils \
external/scrypt/lib/crypto \
@@ -32,7 +33,8 @@ LOCAL_C_INCLUDES := system/extras/ext4_utils \
system/core/init/ \
system/vold/model \
system/vold/ \
system/extras/f2fs_utils/
system/extras/f2fs_utils/ \
bootable/recovery/bootloader_message/include
ifneq ($(wildcard hardware/libhardware/include/hardware/keymaster0.h),)
LOCAL_CFLAGS += -DTW_CRYPTO_HAVE_KEYMASTERX
@@ -43,7 +45,16 @@ LOCAL_REQUIRED_MODULES := keystore_auth keystore
LOCAL_CLANG := true
include $(BUILD_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := fscryptpolicyget
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/system/bin
LOCAL_C_INCLUDES += system/extras/libfscrypt/include
LOCAL_SRC_FILES := fscryptpolicyget.cpp
LOCAL_SHARED_LIBRARIES := libtwrpfscrypt
include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
LOCAL_MODULE := twrpfbe
@@ -55,16 +66,6 @@ LOCAL_SHARED_LIBRARIES := libtwrpfscrypt
include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
LOCAL_MODULE := fscryptpolicyget
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/system/bin
LOCAL_SRC_FILES := fscryptpolicyget.cpp
LOCAL_SHARED_LIBRARIES := libtwrpfscrypt
include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
LOCAL_MODULE := keystore_auth
LOCAL_MODULE_TAGS := optional
+743
View File
@@ -0,0 +1,743 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "Checkpoint"
#include "Checkpoint.h"
#include "VoldUtil.h"
#include "VolumeManager.h"
#include <fstream>
#include <list>
#include <memory>
#include <string>
#include <thread>
#include <vector>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/parseint.h>
#include <android-base/properties.h>
#include <android-base/unique_fd.h>
#include <android/hardware/boot/1.0/IBootControl.h>
#include <cutils/android_reboot.h>
#include <fcntl.h>
#include <fs_mgr.h>
#include <linux/fs.h>
#include <mntent.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <unistd.h>
using android::base::GetBoolProperty;
using android::base::GetUintProperty;
using android::base::SetProperty;
using android::binder::Status;
using android::fs_mgr::Fstab;
using android::fs_mgr::ReadDefaultFstab;
using android::fs_mgr::ReadFstabFromFile;
using android::hardware::hidl_string;
using android::hardware::boot::V1_0::BoolResult;
using android::hardware::boot::V1_0::CommandResult;
using android::hardware::boot::V1_0::IBootControl;
using android::hardware::boot::V1_0::Slot;
namespace {
const std::string kMetadataCPFile = "/metadata/vold/checkpoint";
android::binder::Status error(const std::string& msg) {
PLOG(ERROR) << msg;
return android::binder::Status::fromServiceSpecificError(errno, android::String8(msg.c_str()));
}
android::binder::Status error(int error, const std::string& msg) {
LOG(ERROR) << msg;
return android::binder::Status::fromServiceSpecificError(error, android::String8(msg.c_str()));
}
bool setBowState(std::string const& block_device, std::string const& state) {
std::string bow_device = fs_mgr_find_bow_device(block_device);
if (bow_device.empty()) return false;
if (!android::base::WriteStringToFile(state, bow_device + "/bow/state")) {
PLOG(ERROR) << "Failed to write to file " << bow_device + "/bow/state";
return false;
}
return true;
}
} // namespace
Status cp_supportsCheckpoint(bool& result) {
result = false;
for (const auto& entry : fstab_default) {
if (entry.fs_mgr_flags.checkpoint_blk || entry.fs_mgr_flags.checkpoint_fs) {
result = true;
return Status::ok();
}
}
return Status::ok();
}
Status cp_supportsBlockCheckpoint(bool& result) {
result = false;
for (const auto& entry : fstab_default) {
if (entry.fs_mgr_flags.checkpoint_blk) {
result = true;
return Status::ok();
}
}
return Status::ok();
}
Status cp_supportsFileCheckpoint(bool& result) {
result = false;
for (const auto& entry : fstab_default) {
if (entry.fs_mgr_flags.checkpoint_fs) {
result = true;
return Status::ok();
}
}
return Status::ok();
}
Status cp_startCheckpoint(int retry) {
bool result;
if (!cp_supportsCheckpoint(result).isOk() || !result)
return error(ENOTSUP, "Checkpoints not supported");
if (retry < -1) return error(EINVAL, "Retry count must be more than -1");
std::string content = std::to_string(retry + 1);
if (retry == -1) {
android::sp<IBootControl> module = IBootControl::getService();
if (module) {
std::string suffix;
auto cb = [&suffix](hidl_string s) { suffix = s; };
if (module->getSuffix(module->getCurrentSlot(), cb).isOk()) content += " " + suffix;
}
}
if (!android::base::WriteStringToFile(content, kMetadataCPFile))
return error("Failed to write checkpoint file");
return Status::ok();
}
namespace {
volatile bool isCheckpointing = false;
volatile bool needsCheckpointWasCalled = false;
// Protects isCheckpointing, needsCheckpointWasCalled and code that makes decisions based on status
// of isCheckpointing
std::mutex isCheckpointingLock;
}
Status cp_commitChanges() {
std::lock_guard<std::mutex> lock(isCheckpointingLock);
if (!isCheckpointing) {
return Status::ok();
}
if (android::base::GetProperty("persist.vold.dont_commit_checkpoint", "0") == "1") {
LOG(WARNING)
<< "NOT COMMITTING CHECKPOINT BECAUSE persist.vold.dont_commit_checkpoint IS 1";
return Status::ok();
}
android::sp<IBootControl> module = IBootControl::getService();
if (module) {
CommandResult cr;
module->markBootSuccessful([&cr](CommandResult result) { cr = result; });
if (!cr.success)
return error(EINVAL, "Error marking booted successfully: " + std::string(cr.errMsg));
LOG(INFO) << "Marked slot as booted successfully.";
// Clears the warm reset flag for next reboot.
if (!SetProperty("ota.warm_reset", "0")) {
LOG(WARNING) << "Failed to reset the warm reset flag";
}
}
// Must take action for list of mounted checkpointed things here
// To do this, we walk the list of mounted file systems.
// But we also need to get the matching fstab entries to see
// the original flags
std::string err_str;
Fstab mounts;
if (!ReadFstabFromFile("/proc/mounts", &mounts)) {
return error(EINVAL, "Failed to get /proc/mounts");
}
// Walk mounted file systems
for (const auto& mount_rec : mounts) {
const auto fstab_rec = GetEntryForMountPoint(&fstab_default, mount_rec.mount_point);
if (!fstab_rec) continue;
if (fstab_rec->fs_mgr_flags.checkpoint_fs) {
if (fstab_rec->fs_type == "f2fs") {
std::string options = mount_rec.fs_options + ",checkpoint=enable";
if (mount(mount_rec.blk_device.c_str(), mount_rec.mount_point.c_str(), "none",
MS_REMOUNT | fstab_rec->flags, options.c_str())) {
return error(EINVAL, "Failed to remount");
}
}
} else if (fstab_rec->fs_mgr_flags.checkpoint_blk) {
if (!setBowState(mount_rec.blk_device, "2"))
return error(EINVAL, "Failed to set bow state");
}
}
SetProperty("vold.checkpoint_committed", "1");
LOG(INFO) << "Checkpoint has been committed.";
isCheckpointing = false;
if (!android::base::RemoveFileIfExists(kMetadataCPFile, &err_str))
return error(err_str.c_str());
return Status::ok();
}
namespace {
void abort_metadata_file() {
std::string oldContent, newContent;
int retry = 0;
struct stat st;
int result = stat(kMetadataCPFile.c_str(), &st);
// If the file doesn't exist, we aren't managing a checkpoint retry counter
if (result != 0) return;
if (!android::base::ReadFileToString(kMetadataCPFile, &oldContent)) {
PLOG(ERROR) << "Failed to read checkpoint file";
return;
}
std::string retryContent = oldContent.substr(0, oldContent.find_first_of(" "));
if (!android::base::ParseInt(retryContent, &retry)) {
PLOG(ERROR) << "Could not parse retry count";
return;
}
if (retry > 0) {
newContent = "0";
if (!android::base::WriteStringToFile(newContent, kMetadataCPFile))
PLOG(ERROR) << "Could not write checkpoint file";
}
}
} // namespace
void cp_abortChanges(const std::string& message, bool retry) {
if (!cp_needsCheckpoint()) return;
if (!retry) abort_metadata_file();
android_reboot(ANDROID_RB_RESTART2, 0, message.c_str());
}
bool cp_needsRollback() {
std::string content;
bool ret;
ret = android::base::ReadFileToString(kMetadataCPFile, &content);
if (ret) {
if (content == "0") return true;
if (content.substr(0, 3) == "-1 ") {
std::string oldSuffix = content.substr(3);
android::sp<IBootControl> module = IBootControl::getService();
std::string newSuffix;
if (module) {
auto cb = [&newSuffix](hidl_string s) { newSuffix = s; };
module->getSuffix(module->getCurrentSlot(), cb);
if (oldSuffix == newSuffix) return true;
}
}
}
return false;
}
bool cp_needsCheckpoint() {
std::lock_guard<std::mutex> lock(isCheckpointingLock);
// Make sure we only return true during boot. See b/138952436 for discussion
if (needsCheckpointWasCalled) return isCheckpointing;
needsCheckpointWasCalled = true;
bool ret;
std::string content;
android::sp<IBootControl> module = IBootControl::getService();
if (isCheckpointing) return isCheckpointing;
if (module && module->isSlotMarkedSuccessful(module->getCurrentSlot()) == BoolResult::FALSE) {
isCheckpointing = true;
return true;
}
ret = android::base::ReadFileToString(kMetadataCPFile, &content);
if (ret) {
ret = content != "0";
isCheckpointing = ret;
return ret;
}
return false;
}
bool cp_isCheckpointing() {
return isCheckpointing;
}
namespace {
const std::string kSleepTimeProp = "ro.sys.cp_msleeptime";
const uint32_t msleeptime_default = 1000; // 1 s
const uint32_t max_msleeptime = 3600000; // 1 h
const std::string kMinFreeBytesProp = "ro.sys.cp_min_free_bytes";
const uint64_t min_free_bytes_default = 100 * (1 << 20); // 100 MiB
const std::string kCommitOnFullProp = "ro.sys.cp_commit_on_full";
const bool commit_on_full_default = true;
static void cp_healthDaemon(std::string mnt_pnt, std::string blk_device, bool is_fs_cp) {
struct statvfs data;
uint32_t msleeptime = GetUintProperty(kSleepTimeProp, msleeptime_default, max_msleeptime);
uint64_t min_free_bytes =
GetUintProperty(kMinFreeBytesProp, min_free_bytes_default, (uint64_t)-1);
bool commit_on_full = GetBoolProperty(kCommitOnFullProp, commit_on_full_default);
struct timespec req;
req.tv_sec = msleeptime / 1000;
msleeptime %= 1000;
req.tv_nsec = msleeptime * 1000000;
while (isCheckpointing) {
uint64_t free_bytes = 0;
if (is_fs_cp) {
statvfs(mnt_pnt.c_str(), &data);
free_bytes = ((uint64_t) data.f_bavail) * data.f_frsize;
} else {
std::string bow_device = fs_mgr_find_bow_device(blk_device);
if (!bow_device.empty()) {
std::string content;
if (android::base::ReadFileToString(bow_device + "/bow/free", &content)) {
free_bytes = std::strtoull(content.c_str(), NULL, 10);
}
}
}
if (free_bytes < min_free_bytes) {
if (commit_on_full) {
LOG(INFO) << "Low space for checkpointing. Commiting changes";
cp_commitChanges();
break;
} else {
LOG(INFO) << "Low space for checkpointing. Rebooting";
cp_abortChanges("checkpoint,low_space", false);
break;
}
}
nanosleep(&req, NULL);
}
}
} // namespace
Status cp_prepareCheckpoint() {
// Log to notify CTS - see b/137924328 for context
LOG(INFO) << "cp_prepareCheckpoint called";
std::lock_guard<std::mutex> lock(isCheckpointingLock);
if (!isCheckpointing) {
return Status::ok();
}
Fstab mounts;
if (!ReadFstabFromFile("/proc/mounts", &mounts)) {
return error(EINVAL, "Failed to get /proc/mounts");
}
for (const auto& mount_rec : mounts) {
const auto fstab_rec = GetEntryForMountPoint(&fstab_default, mount_rec.mount_point);
if (!fstab_rec) continue;
if (fstab_rec->fs_mgr_flags.checkpoint_blk) {
android::base::unique_fd fd(
TEMP_FAILURE_RETRY(open(mount_rec.mount_point.c_str(), O_RDONLY | O_CLOEXEC)));
if (fd == -1) {
PLOG(ERROR) << "Failed to open mount point" << mount_rec.mount_point;
continue;
}
struct fstrim_range range = {};
range.len = ULLONG_MAX;
nsecs_t start = systemTime(SYSTEM_TIME_BOOTTIME);
if (ioctl(fd, FITRIM, &range)) {
PLOG(ERROR) << "Failed to trim " << mount_rec.mount_point;
continue;
}
nsecs_t time = systemTime(SYSTEM_TIME_BOOTTIME) - start;
LOG(INFO) << "Trimmed " << range.len << " bytes on " << mount_rec.mount_point << " in "
<< nanoseconds_to_milliseconds(time) << "ms for checkpoint";
setBowState(mount_rec.blk_device, "1");
}
if (fstab_rec->fs_mgr_flags.checkpoint_blk || fstab_rec->fs_mgr_flags.checkpoint_fs) {
std::thread(cp_healthDaemon, std::string(mount_rec.mount_point),
std::string(mount_rec.blk_device),
fstab_rec->fs_mgr_flags.checkpoint_fs == 1)
.detach();
}
}
return Status::ok();
}
namespace {
const int kSectorSize = 512;
typedef uint64_t sector_t;
struct log_entry {
sector_t source; // in sectors of size kSectorSize
sector_t dest; // in sectors of size kSectorSize
uint32_t size; // in bytes
uint32_t checksum;
} __attribute__((packed));
struct log_sector_v1_0 {
uint32_t magic;
uint16_t header_version;
uint16_t header_size;
uint32_t block_size;
uint32_t count;
uint32_t sequence;
uint64_t sector0;
} __attribute__((packed));
// MAGIC is BOW in ascii
const int kMagic = 0x00574f42;
// Partially restored MAGIC is WOB in ascii
const int kPartialRestoreMagic = 0x00424f57;
void crc32(const void* data, size_t n_bytes, uint32_t* crc) {
static uint32_t table[0x100] = {
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535,
0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD,
0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D,
0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4,
0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C,
0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC,
0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB,
0xB6662D3D,
0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5,
0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D,
0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED,
0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,
0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 0x4DB26158, 0x3AB551CE, 0xA3BC0074,
0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC,
0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C,
0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B,
0xC0BA6CAD,
0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615,
0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D,
0x0A00AE27, 0x7D079EB1, 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D,
0x806567CB, 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4,
0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 0xD80D2BDA, 0xAF0A1B4C,
0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, 0xCB61B38C,
0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B,
0x5BDEAE1D,
0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785,
0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D,
0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD,
0xF6B9265B, 0x6FB077E1, 0x18B74777, 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354,
0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC,
0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C,
0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B,
0x2D02EF8D};
for (size_t i = 0; i < n_bytes; ++i) {
*crc ^= ((uint8_t*)data)[i];
*crc = table[(uint8_t)*crc] ^ *crc >> 8;
}
}
// A map of relocations.
// The map must be initialized so that relocations[0] = 0
// During restore, we replay the log records in reverse, copying from dest to
// source
// To validate, we must be able to read the 'dest' sectors as though they had
// been copied but without actually copying. This map represents how the sectors
// would have been moved. To read a sector s, find the index <= s and read
// relocations[index] + s - index
typedef std::map<sector_t, sector_t> Relocations;
void relocate(Relocations& relocations, sector_t dest, sector_t source, int count) {
// Find first one we're equal to or greater than
auto s = --relocations.upper_bound(source);
// Take slice
Relocations slice;
slice[dest] = source - s->first + s->second;
++s;
// Add rest of elements
for (; s != relocations.end() && s->first < source + count; ++s)
slice[dest - source + s->first] = s->second;
// Split range at end of dest
auto dest_end = --relocations.upper_bound(dest + count);
relocations[dest + count] = dest + count - dest_end->first + dest_end->second;
// Remove all elements in [dest, dest + count)
relocations.erase(relocations.lower_bound(dest), relocations.lower_bound(dest + count));
// Add new elements
relocations.insert(slice.begin(), slice.end());
}
// A map of sectors that have been written to.
// The final entry must always be False.
// When we restart the restore after an interruption, we must take care that
// when we copy from dest to source, that the block we copy to was not
// previously copied from.
// i e. A->B C->A; If we replay this sequence, we end up copying C->B
// We must save our partial result whenever we finish a page, or when we copy
// to a location that was copied from earlier (our source is an earlier dest)
typedef std::map<sector_t, bool> Used_Sectors;
bool checkCollision(Used_Sectors& used_sectors, sector_t start, sector_t end) {
auto second_overlap = used_sectors.upper_bound(start);
auto first_overlap = --second_overlap;
if (first_overlap->second) {
return true;
} else if (second_overlap != used_sectors.end() && second_overlap->first < end) {
return true;
}
return false;
}
void markUsed(Used_Sectors& used_sectors, sector_t start, sector_t end) {
auto start_pos = used_sectors.insert_or_assign(start, true).first;
auto end_pos = used_sectors.insert_or_assign(end, false).first;
if (start_pos == used_sectors.begin() || !std::prev(start_pos)->second) {
start_pos++;
}
if (std::next(end_pos) != used_sectors.end() && !std::next(end_pos)->second) {
end_pos++;
}
if (start_pos->first < end_pos->first) {
used_sectors.erase(start_pos, end_pos);
}
}
// Restores the given log_entry's data from dest -> source
// If that entry is a log sector, set the magic to kPartialRestoreMagic and flush.
void restoreSector(int device_fd, Used_Sectors& used_sectors, std::vector<char>& ls_buffer,
log_entry* le, std::vector<char>& buffer) {
log_sector_v1_0& ls = *reinterpret_cast<log_sector_v1_0*>(&ls_buffer[0]);
uint32_t index = le - ((log_entry*)&ls_buffer[ls.header_size]);
int count = (le->size - 1) / kSectorSize + 1;
if (checkCollision(used_sectors, le->source, le->source + count)) {
fsync(device_fd);
lseek64(device_fd, 0, SEEK_SET);
ls.count = index + 1;
ls.magic = kPartialRestoreMagic;
write(device_fd, &ls_buffer[0], ls.block_size);
fsync(device_fd);
used_sectors.clear();
used_sectors[0] = false;
}
markUsed(used_sectors, le->dest, le->dest + count);
if (index == 0 && ls.sequence != 0) {
log_sector_v1_0* next = reinterpret_cast<log_sector_v1_0*>(&buffer[0]);
if (next->magic == kMagic) {
next->magic = kPartialRestoreMagic;
}
}
lseek64(device_fd, le->source * kSectorSize, SEEK_SET);
write(device_fd, &buffer[0], le->size);
if (index == 0) {
fsync(device_fd);
}
}
// Read from the device
// If we are validating, the read occurs as though the relocations had happened
std::vector<char> relocatedRead(int device_fd, Relocations const& relocations, bool validating,
sector_t sector, uint32_t size, uint32_t block_size) {
if (!validating) {
std::vector<char> buffer(size);
lseek64(device_fd, sector * kSectorSize, SEEK_SET);
read(device_fd, &buffer[0], size);
return buffer;
}
std::vector<char> buffer(size);
for (uint32_t i = 0; i < size; i += block_size, sector += block_size / kSectorSize) {
auto relocation = --relocations.upper_bound(sector);
lseek64(device_fd, (sector + relocation->second - relocation->first) * kSectorSize,
SEEK_SET);
read(device_fd, &buffer[i], block_size);
}
return buffer;
}
} // namespace
Status cp_restoreCheckpoint(const std::string& blockDevice, int restore_limit) {
bool validating = true;
std::string action = "Validating";
int restore_count = 0;
for (;;) {
Relocations relocations;
relocations[0] = 0;
Status status = Status::ok();
LOG(INFO) << action << " checkpoint on " << blockDevice;
android::base::unique_fd device_fd(open(blockDevice.c_str(), O_RDWR | O_CLOEXEC));
if (device_fd < 0) return error("Cannot open " + blockDevice);
log_sector_v1_0 original_ls;
read(device_fd, reinterpret_cast<char*>(&original_ls), sizeof(original_ls));
if (original_ls.magic == kPartialRestoreMagic) {
validating = false;
action = "Restoring";
} else if (original_ls.magic != kMagic) {
return error(EINVAL, "No magic");
}
LOG(INFO) << action << " " << original_ls.sequence << " log sectors";
for (int sequence = original_ls.sequence; sequence >= 0 && status.isOk(); sequence--) {
auto ls_buffer = relocatedRead(device_fd, relocations, validating, 0,
original_ls.block_size, original_ls.block_size);
log_sector_v1_0& ls = *reinterpret_cast<log_sector_v1_0*>(&ls_buffer[0]);
Used_Sectors used_sectors;
used_sectors[0] = false;
if (ls.magic != kMagic && (ls.magic != kPartialRestoreMagic || validating)) {
status = error(EINVAL, "No magic");
break;
}
if (ls.block_size != original_ls.block_size) {
status = error(EINVAL, "Block size mismatch");
break;
}
if ((int)ls.sequence != sequence) {
status = error(EINVAL, "Expecting log sector " + std::to_string(sequence) +
" but got " + std::to_string(ls.sequence));
break;
}
LOG(INFO) << action << " from log sector " << ls.sequence;
for (log_entry* le =
reinterpret_cast<log_entry*>(&ls_buffer[ls.header_size]) + ls.count - 1;
le >= reinterpret_cast<log_entry*>(&ls_buffer[ls.header_size]); --le) {
// This is very noisy - limit to DEBUG only
LOG(VERBOSE) << action << " " << le->size << " bytes from sector " << le->dest
<< " to " << le->source << " with checksum " << std::hex
<< le->checksum;
auto buffer = relocatedRead(device_fd, relocations, validating, le->dest, le->size,
ls.block_size);
uint32_t checksum = le->source / (ls.block_size / kSectorSize);
for (size_t i = 0; i < le->size; i += ls.block_size) {
crc32(&buffer[i], ls.block_size, &checksum);
}
if (le->checksum && checksum != le->checksum) {
status = error(EINVAL, "Checksums don't match");
break;
}
if (validating) {
relocate(relocations, le->source, le->dest, (le->size - 1) / kSectorSize + 1);
} else {
restoreSector(device_fd, used_sectors, ls_buffer, le, buffer);
restore_count++;
if (restore_limit && restore_count >= restore_limit) {
status = error(EAGAIN, "Hit the test limit");
break;
}
}
}
}
if (!status.isOk()) {
if (!validating) {
LOG(ERROR) << "Checkpoint restore failed even though checkpoint validation passed";
return status;
}
LOG(WARNING) << "Checkpoint validation failed - attempting to roll forward";
auto buffer = relocatedRead(device_fd, relocations, false, original_ls.sector0,
original_ls.block_size, original_ls.block_size);
lseek64(device_fd, 0, SEEK_SET);
write(device_fd, &buffer[0], original_ls.block_size);
return Status::ok();
}
if (!validating) break;
validating = false;
action = "Restoring";
}
return Status::ok();
}
Status cp_markBootAttempt() {
std::string oldContent, newContent;
int retry = 0;
struct stat st;
int result = stat(kMetadataCPFile.c_str(), &st);
// If the file doesn't exist, we aren't managing a checkpoint retry counter
if (result != 0) return Status::ok();
if (!android::base::ReadFileToString(kMetadataCPFile, &oldContent))
return error("Failed to read checkpoint file");
std::string retryContent = oldContent.substr(0, oldContent.find_first_of(" "));
if (!android::base::ParseInt(retryContent, &retry))
return error(EINVAL, "Could not parse retry count");
if (retry > 0) {
retry--;
newContent = std::to_string(retry);
if (!android::base::WriteStringToFile(newContent, kMetadataCPFile))
return error("Could not write checkpoint file");
}
return Status::ok();
}
void cp_resetCheckpoint() {
std::lock_guard<std::mutex> lock(isCheckpointingLock);
needsCheckpointWasCalled = false;
}
+3 -4
View File
@@ -20,8 +20,6 @@
#include <binder/Status.h>
#include <string>
namespace android {
namespace vold {
android::binder::Status cp_supportsCheckpoint(bool& result);
@@ -39,13 +37,14 @@ bool cp_needsRollback();
bool cp_needsCheckpoint();
bool cp_isCheckpointing();
android::binder::Status cp_prepareCheckpoint();
android::binder::Status cp_restoreCheckpoint(const std::string& mountPoint, int count = 0);
android::binder::Status cp_markBootAttempt();
} // namespace vold
} // namespace android
void cp_resetCheckpoint();
#endif
+38
View File
@@ -0,0 +1,38 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "CryptoType.h"
#include <string.h>
#include <android-base/logging.h>
#include <cutils/properties.h>
const CryptoType& lookup_crypto_algorithm(const CryptoType table[], int table_len,
const CryptoType& default_alg, const char* property) {
char paramstr[PROPERTY_VALUE_MAX];
property_get(property, paramstr, default_alg.get_config_name());
for (int i = 0; i < table_len; i++) {
if (strcmp(paramstr, table[i].get_config_name()) == 0) {
return table[i];
}
}
LOG(ERROR) << "Invalid name (" << paramstr << ") for " << property << ". Defaulting to "
<< default_alg.get_config_name() << ".";
return default_alg;
}
+98
View File
@@ -0,0 +1,98 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <stdlib.h>
// Struct representing an encryption algorithm supported by vold.
// "config_name" represents the name we give the algorithm in
// read-only properties and fstab files
// "kernel_name" is the name we present to the Linux kernel
// "keysize" is the size of the key in bytes.
struct CryptoType {
// We should only be constructing CryptoTypes as part of
// supported_crypto_types[]. We do it via this pseudo-builder pattern,
// which isn't pure or fully protected as a concession to being able to
// do it all at compile time. Add new CryptoTypes in
// supported_crypto_types[] below.
constexpr CryptoType() : CryptoType(nullptr, nullptr, 0xFFFFFFFF) {}
constexpr CryptoType set_keysize(size_t size) const {
return CryptoType(this->config_name, this->kernel_name, size);
}
constexpr CryptoType set_config_name(const char* property) const {
return CryptoType(property, this->kernel_name, this->keysize);
}
constexpr CryptoType set_kernel_name(const char* crypto) const {
return CryptoType(this->config_name, crypto, this->keysize);
}
constexpr const char* get_config_name() const { return config_name; }
constexpr const char* get_kernel_name() const { return kernel_name; }
constexpr size_t get_keysize() const { return keysize; }
private:
const char* config_name;
const char* kernel_name;
size_t keysize;
constexpr CryptoType(const char* property, const char* crypto, size_t ksize)
: config_name(property), kernel_name(crypto), keysize(ksize) {}
};
// Use the named android property to look up a type from the table
// If the property is not set or matches no table entry, return the default.
const CryptoType& lookup_crypto_algorithm(const CryptoType table[], int table_len,
const CryptoType& default_alg, const char* property);
// Some useful types
constexpr CryptoType invalid_crypto_type = CryptoType();
constexpr CryptoType aes_256_xts = CryptoType()
.set_config_name("aes-256-xts")
.set_kernel_name("aes-xts-plain64")
.set_keysize(64);
constexpr CryptoType adiantum = CryptoType()
.set_config_name("adiantum")
.set_kernel_name("xchacha12,aes-adiantum-plain64")
.set_keysize(32);
// Support compile-time validation of a crypto type table
template <typename T, size_t N>
constexpr size_t array_length(T (&)[N]) {
return N;
}
constexpr bool isValidCryptoType(size_t max_keylen, const CryptoType& crypto_type) {
return ((crypto_type.get_config_name() != nullptr) &&
(crypto_type.get_kernel_name() != nullptr) &&
(crypto_type.get_keysize() <= max_keylen));
}
// Confirms that all supported_crypto_types have a small enough keysize and
// had both set_config_name() and set_kernel_name() called.
// Note in C++11 that constexpr functions can only have a single line.
// So our code is a bit convoluted (using recursion instead of a loop),
// but it's asserting at compile time that all of our key lengths are valid.
constexpr bool validateSupportedCryptoTypes(size_t max_keylen, const CryptoType types[],
size_t len) {
return len == 0 || (isValidCryptoType(max_keylen, types[len - 1]) &&
validateSupportedCryptoTypes(max_keylen, types, len - 1));
}
+53 -53
View File
@@ -16,6 +16,7 @@
#include "Decrypt.h"
#include "FsCrypt.h"
#include <fscrypt/fscrypt.h>
#include <map>
#include <string>
@@ -72,39 +73,37 @@
#include <keystore/OperationResult.h>
#include "keystore_client.pb.h"
#include <keymasterV4_0/authorization_set.h>
#include <keymasterV4_0/keymaster_utils.h>
#include <keymasterV4_1/authorization_set.h>
#include <keymasterV4_1/keymaster_utils.h>
extern "C" {
#include "crypto_scrypt.h"
}
#include "fscrypt_policy.h"
#include "fscrypt-common.h"
#include "HashPassword.h"
#include "KeyStorage.h"
#include "android/os/IVold.h"
using android::security::keystore::IKeystoreService;
using keystore::KeystoreResponsePromise;
using keystore::OperationResultPromise;
using android::security::keymaster::OperationResult;
using android::hardware::keymaster::V4_0::support::blob2hidlVec;
using android::hardware::keymaster::V4_1::support::blob2hidlVec;
// Store main DE raw ref / policy
extern std::string de_raw_ref;
extern std::map<userid_t, std::string> s_de_key_raw_refs;
extern std::map<userid_t, std::string> s_ce_key_raw_refs;
inline std::string hidlVec2String(const ::keystore::hidl_vec<uint8_t>& value) {
return std::string(reinterpret_cast<const std::string::value_type*>(&value[0]), value.size());
}
static bool lookup_ref_key_internal(std::map<userid_t, std::string>& key_map, const uint8_t* policy, userid_t* user_id) {
char policy_string_hex[FS_KEY_DESCRIPTOR_SIZE_HEX];
char key_map_hex[FS_KEY_DESCRIPTOR_SIZE_HEX];
policy_to_hex(policy, policy_string_hex);
static bool lookup_ref_key_internal(std::map<userid_t, android::fscrypt::EncryptionPolicy> key_map, const uint8_t* policy, userid_t* user_id) {
char policy_string_hex[FSCRYPT_KEY_IDENTIFIER_HEX_SIZE];
char key_map_hex[FSCRYPT_KEY_IDENTIFIER_HEX_SIZE];
bytes_to_hex(policy, FSCRYPT_KEY_IDENTIFIER_SIZE, policy_string_hex);
for (std::map<userid_t, std::string>::iterator it=key_map.begin(); it!=key_map.end(); ++it) {
policy_to_hex(reinterpret_cast<const uint8_t*>(&it->second[0]), key_map_hex);
for (std::map<userid_t, android::fscrypt::EncryptionPolicy>::iterator it=key_map.begin(); it!=key_map.end(); ++it) {
bytes_to_hex(reinterpret_cast<const uint8_t*>(&it->second.key_raw_ref[0]), FSCRYPT_KEY_IDENTIFIER_SIZE, key_map_hex);
std::string key_map_hex_string = std::string(key_map_hex);
if (key_map_hex_string == policy_string_hex) {
*user_id = it->first;
@@ -114,65 +113,72 @@ static bool lookup_ref_key_internal(std::map<userid_t, std::string>& key_map, co
return false;
}
extern "C" bool lookup_ref_key(const uint8_t* policy, uint8_t* policy_type) {
userid_t user_id = 0;
char policy_string_hex[FS_KEY_DESCRIPTOR_SIZE_HEX];
char de_raw_ref_hex[FS_KEY_DESCRIPTOR_SIZE_HEX];
policy_to_hex(policy, policy_string_hex);
policy_to_hex(reinterpret_cast<const uint8_t*>(&de_raw_ref[0]), de_raw_ref_hex);
std::string de_raw_ref_hex_string = std::string(de_raw_ref_hex);
#ifdef USE_FSCRYPT_POLICY_V1
extern "C" bool lookup_ref_key(fscrypt_policy_v1* v1, uint8_t* policy_type) {
#else
extern "C" bool lookup_ref_key(fscrypt_policy_v2* v2, uint8_t* policy_type) {
#endif
userid_t user_id = 0;
std::string policy_type_string;
if (policy_string_hex == de_raw_ref_hex_string) {
char policy_hex[FSCRYPT_KEY_IDENTIFIER_HEX_SIZE];
bytes_to_hex(v2->master_key_identifier, FSCRYPT_KEY_IDENTIFIER_SIZE, policy_hex);
if (std::strncmp((const char*) v2->master_key_identifier, de_key_raw_ref.c_str(), FSCRYPT_KEY_IDENTIFIER_SIZE) == 0) {
policy_type_string = "0DK";
memcpy(policy_type, policy_type_string.data(), policy_type_string.size());
return true;
}
if (!lookup_ref_key_internal(s_de_key_raw_refs, policy, &user_id)) {
if (!lookup_ref_key_internal(s_ce_key_raw_refs, policy, &user_id)) {
if (!lookup_ref_key_internal(s_de_policies, v2->master_key_identifier, &user_id)) {
if (!lookup_ref_key_internal(s_ce_policies, v2->master_key_identifier, &user_id)) {
return false;
} else
} else {
policy_type_string = "0CE" + std::to_string(user_id);
} else
}
} else {
policy_type_string = "0DE" + std::to_string(user_id);
}
memcpy(policy_type, policy_type_string.data(), policy_type_string.size());
LOG(INFO) << "storing policy type: " << policy_type;
return true;
}
extern "C" bool lookup_ref_tar(const uint8_t* policy_type, uint8_t* policy) {
std::string policy_type_string = std::string((char *) policy_type);
char policy_hex[FS_KEY_DESCRIPTOR_SIZE_HEX];
policy_to_hex(policy_type, policy_hex);
char policy_hex[FSCRYPT_KEY_IDENTIFIER_HEX_SIZE];
bytes_to_hex(policy_type, FSCRYPT_KEY_IDENTIFIER_SIZE, policy_hex);
// Current encryption fscrypt policy is v1 (which is stored as version 0e)
userid_t user_id = atoi(policy_type_string.substr(3, 4).c_str());
// TODO Update version # and make magic strings
if (policy_type_string.substr(0,1) != "0") {
printf("Unexpected version %c\n", policy_type[0]);
LOG(ERROR) << "Unexpected version:" << policy_type[0];
return false;
}
if (policy_type_string.substr(1, 2) == "DK") {
memcpy(policy, de_raw_ref.data(), de_raw_ref.size());
memcpy(policy, de_key_raw_ref.data(), de_key_raw_ref.size());
return true;
}
userid_t user_id = atoi(policy_type_string.substr(3, 4).c_str());
std::string raw_ref;
if (policy_type_string.substr(1, 1) == "D") {
if (lookup_key_ref(s_de_key_raw_refs, user_id, &raw_ref)) {
if (lookup_key_ref(s_de_policies, user_id, &raw_ref)) {
memcpy(policy, raw_ref.data(), raw_ref.size());
} else
return false;
} else if (policy_type_string.substr(1, 1) == "C") {
if (lookup_key_ref(s_ce_key_raw_refs, user_id, &raw_ref)) {
if (lookup_key_ref(s_ce_policies, user_id, &raw_ref)) {
memcpy(policy, raw_ref.data(), raw_ref.size());
} else
return false;
} else {
printf("unknown policy type '%s'\n", policy_type);
LOG(ERROR) << "unknown policy type: " << policy_type;
return false;
}
char found_policy_hex[FSCRYPT_KEY_IDENTIFIER_HEX_SIZE];
bytes_to_hex(policy, FSCRYPT_KEY_IDENTIFIER_SIZE, found_policy_hex);
return true;
}
@@ -688,14 +694,16 @@ std::string unwrapSyntheticPasswordBlob(const std::string& spblob_path, const st
return disk_decryption_secret_key;
}
OperationResult result = future.get();
auto handle = std::move(result.token);
std::map<uint64_t, android::sp<android::IBinder>> active_operations_;
uint64_t next_virtual_handle_ = 1;
active_operations_[next_virtual_handle_] = result.token;
// The cipher.doFinal call triggers an update to the keystore followed by a finish https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java#64
// See also https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java#208
future = {};
promise = new OperationResultPromise();
future = promise->get_future();
binder_result = service->update(promise, handle, empty_params, cipher_text_hidlvec, &error_code);
binder_result = service->update(promise, active_operations_[next_virtual_handle_], empty_params, cipher_text_hidlvec, &error_code);
rc = ::keystore::KeyStoreNativeReturnCode(error_code);
if (!rc.isOk()) {
printf("Keystore update returned: %d\n", error_code);
@@ -717,11 +725,10 @@ std::string unwrapSyntheticPasswordBlob(const std::string& spblob_path, const st
future = {};
promise = new OperationResultPromise();
future = promise->get_future();
std::tie(rc, keyBlob, charBlob, lockedEntry) = mKeyStore->getKeyForName(name8, callingUid, TYPE_KEYMASTER_10);
auto hidlInput = blob2hidlVec(input_data);
::keystore::hidl_vec<uint8_t> signature;
binder_result = service->finish(promise, handle, empty_params, hidlInput, signature, entropy, &error_code);
auto hidlSignature = blob2hidlVec("");
auto hidlInput = blob2hidlVec(disk_decryption_secret_key);
binder_result = service->finish(promise, active_operations_[next_virtual_handle_], empty_params, hidlInput, hidlSignature, ::keystore::hidl_vec<uint8_t>(), &error_code);
if (!binder_result.isOk()) {
printf("communication error while calling keystore\n");
free(keystore_result);
@@ -844,11 +851,7 @@ bool Decrypt_User_Synth_Pass(const userid_t user_id, const std::string& Password
std::string secret; // this will be the disk decryption key that is sent to vold
std::string token = "!"; // there is no token used for this kind of decrypt, key escrow is handled by weaver
int flags = FLAG_STORAGE_DE;
if (user_id == 0)
flags = FLAG_STORAGE_DE;
else
flags = FLAG_STORAGE_CE;
int flags = android::os::IVold::STORAGE_FLAG_CE;
char spblob_path_char[PATH_MAX];
sprintf(spblob_path_char, "/data/system_de/%d/spblob/", user_id);
std::string spblob_path = spblob_path_char;
@@ -1091,11 +1094,8 @@ bool Decrypt_User(const userid_t user_id, const std::string& Password) {
printf("Unknown password type\n");
return false;
}
int flags = FLAG_STORAGE_DE;
if (user_id == 0)
flags = FLAG_STORAGE_DE;
else
flags = FLAG_STORAGE_CE;
int flags = android::os::IVold::STORAGE_FLAG_CE;
if (Default_Password) {
if (!fscrypt_unlock_user_key(user_id, 0, "!", "!")) {
+2 -2
View File
@@ -27,8 +27,8 @@ __BEGIN_DECLS
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;
static constexpr int STORAGE_FLAG_DE = 1;
static constexpr int STORAGE_FLAG_CE = 2;
int Get_Password_Type(const userid_t user_id, std::string& filename);
+8 -1
View File
@@ -391,6 +391,8 @@ static int cryptfs_enable_inplace_f2fs(const char* crypto_blkdev, const char* re
struct encryptGroupsData data;
struct f2fs_info* f2fs_info = NULL;
int rc = ENABLE_INPLACE_ERR_OTHER;
struct timespec time_started = {0};
if (previously_encrypted_upto > *size_already_done) {
LOG(DEBUG) << "Not fast encrypting since resuming part way through";
return ENABLE_INPLACE_ERR_OTHER;
@@ -423,9 +425,14 @@ static int cryptfs_enable_inplace_f2fs(const char* crypto_blkdev, const char* re
data.one_pct = data.tot_used_blocks / 100;
data.cur_pct = 0;
data.time_started = time(NULL);
if (clock_gettime(CLOCK_MONOTONIC, &time_started)) {
LOG(WARNING) << "Error getting time at start";
// Note - continue anyway - we'll run with 0
}
data.time_started = time_started.tv_sec;
data.remaining_time = -1;
data.buffer = (char*)malloc(f2fs_info->block_size);
if (!data.buffer) {
LOG(ERROR) << "Failed to allocate crypto buffer";
+5
View File
@@ -24,6 +24,11 @@
#define RETRY_MOUNT_ATTEMPTS 10
#define RETRY_MOUNT_DELAY_SECONDS 1
/* Return values for cryptfs_enable_inplace() */
#define ENABLE_INPLACE_OK 0
#define ENABLE_INPLACE_ERR_OTHER (-1)
#define ENABLE_INPLACE_ERR_DEV (-2) /* crypto_blkdev issue */
int cryptfs_enable_inplace(const char* crypto_blkdev, const char* real_blkdev, off64_t size,
off64_t* size_already_done, off64_t tot_size,
off64_t previously_encrypted_upto, bool set_progress_properties);
+382 -375
View File
File diff suppressed because it is too large Load Diff
+11 -9
View File
@@ -14,12 +14,14 @@
* limitations under the License.
*/
#include <string>
#include <map>
#include <vector>
#include <string>
#include <fscrypt/fscrypt.h>
#include <cutils/multiuser.h>
using namespace android::fscrypt;
bool fscrypt_initialize_systemwide_keys();
bool fscrypt_init_user0();
@@ -27,8 +29,8 @@ bool fscrypt_vold_create_user_key(userid_t user_id, int serial, bool ephemeral);
bool fscrypt_destroy_user_key(userid_t user_id);
bool fscrypt_add_user_key_auth(userid_t user_id, int serial, const std::string& token,
const std::string& secret);
bool fscrypt_clear_user_key_auth(userid_t user_id, int serial, const std::string& token_hex,
const std::string& secret_hex);
bool fscrypt_clear_user_key_auth(userid_t user_id, int serial, const std::string& token,
const std::string& secret);
bool fscrypt_fixate_newest_user_key_auth(userid_t user_id);
bool fscrypt_unlock_user_key(userid_t user_id, int serial, const std::string& token,
@@ -40,8 +42,8 @@ bool fscrypt_prepare_user_storage(const std::string& volume_uuid, userid_t user_
bool fscrypt_destroy_user_storage(const std::string& volume_uuid, userid_t user_id, int flags);
bool fscrypt_destroy_volume_keys(const std::string& volume_uuid);
bool is_wrapped_key_supported();
bool is_wrapped_key_supported_external();
bool is_metadata_wrapped_key_supported();
bool lookup_key_ref(const std::map<userid_t, std::string>& key_map, userid_t user_id,
std::string* raw_ref);
bool lookup_key_ref(const std::map<userid_t, android::fscrypt::EncryptionPolicy>& key_map, userid_t user_id,
std::string* raw_ref);
bool lookup_policy(const std::map<userid_t, EncryptionPolicy>& key_map, userid_t user_id,
EncryptionPolicy* policy);
-5
View File
@@ -21,8 +21,6 @@
#include <memory>
#include <vector>
namespace android {
namespace vold {
/**
* Variant of memset() that should never be optimized away. Borrowed from keymaster code.
@@ -56,8 +54,5 @@ using KeyBuffer = std::vector<char, ZeroingAllocator>;
KeyBuffer operator+(KeyBuffer&& lhs, const KeyBuffer& rhs);
KeyBuffer operator+(KeyBuffer&& lhs, const char* rhs);
} // namespace vold
} // namespace android
#endif
+45 -81
View File
@@ -16,10 +16,10 @@
#include "KeyStorage.h"
#include "Checkpoint.h"
#include "Keymaster.h"
#include "ScryptParameters.h"
#include "Utils.h"
#include "Checkpoint.h"
#include <thread>
#include <vector>
@@ -37,23 +37,20 @@
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/unique_fd.h>
#include <android-base/properties.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 <keymasterV4_1/authorization_set.h>
#include <keymasterV4_1/keymaster_utils.h>
extern "C" {
#include "crypto_scrypt.h"
}
namespace android {
namespace vold {
const KeyAuthentication kEmptyAuthentication{"", ""};
static constexpr size_t AES_KEY_BYTES = 32;
@@ -64,7 +61,6 @@ static constexpr size_t SECDISCARDABLE_BYTES = 1 << 14;
static constexpr size_t STRETCHED_BYTES = 1 << 6;
static constexpr uint32_t AUTH_TIMEOUT = 30; // Seconds
constexpr int EXT4_AES_256_XTS_KEY_SIZE = 64;
static const char* kCurrentVersion = "1";
static const char* kRmPath = "/system/bin/rm";
@@ -123,70 +119,42 @@ static bool generateKeymasterKey(Keymaster& keymaster, const KeyAuthentication&
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);
auto user_id = at->user_id; // Make a copy because at->user_id is unaligned.
paramBuilder.Authorization(km::TAG_USER_SECURE_ID, user_id);
paramBuilder.Authorization(km::TAG_USER_AUTH_TYPE, km::HardwareAuthenticatorType::PASSWORD);
paramBuilder.Authorization(km::TAG_AUTH_TIMEOUT, AUTH_TIMEOUT);
}
return keymaster.generateKey(paramBuilder, key);
auto paramsWithRollback = paramBuilder;
paramsWithRollback.Authorization(km::TAG_ROLLBACK_RESISTANCE);
// Generate rollback-resistant key if possible.
return keymaster.generateKey(paramsWithRollback, key) ||
keymaster.generateKey(paramBuilder, key);
}
bool generateWrappedKey(userid_t user_id, KeyType key_type,
KeyBuffer* key) {
bool generateWrappedStorageKey(KeyBuffer* key) {
Keymaster keymaster;
if (!keymaster) return false;
*key = KeyBuffer(EXT4_AES_256_XTS_KEY_SIZE);
std::string key_temp;
auto paramBuilder = km::AuthorizationSetBuilder()
.AesEncryptionKey(AES_KEY_BYTES * 8)
.GcmModeMinMacLen(GCM_MAC_BYTES * 8)
.Authorization(km::TAG_USER_ID, user_id);
km::KeyParameter param1;
param1.tag = (km::Tag) (android::hardware::keymaster::V4_0::KM_TAG_FBE_ICE);
param1.f.boolValue = true;
paramBuilder.push_back(param1);
km::KeyParameter param2;
if ((key_type == KeyType::DE_USER) || (key_type == KeyType::DE_SYS || (key_type == KeyType::ME))) {
param2.tag = (km::Tag) (android::hardware::keymaster::V4_0::KM_TAG_KEY_TYPE);
param2.f.integer = 0;
} else if (key_type == KeyType::CE_USER) {
param2.tag = (km::Tag) (android::hardware::keymaster::V4_0::KM_TAG_KEY_TYPE);
param2.f.integer = 1;
}
paramBuilder.push_back(param2);
auto paramBuilder = km::AuthorizationSetBuilder().AesEncryptionKey(AES_KEY_BYTES * 8);
paramBuilder.Authorization(km::TAG_ROLLBACK_RESISTANCE);
paramBuilder.Authorization(km::TAG_STORAGE_KEY);
if (!keymaster.generateKey(paramBuilder, &key_temp)) return false;
*key = KeyBuffer(key_temp.size());
memcpy(reinterpret_cast<void*>(key->data()), key_temp.c_str(), key->size());
return true;
}
bool getEphemeralWrappedKey(km::KeyFormat format, KeyBuffer& kmKey, KeyBuffer* key) {
std::string key_temp;
bool exportWrappedStorageKey(const KeyBuffer& kmKey, KeyBuffer* key) {
Keymaster keymaster;
if (!keymaster) return false;
std::string key_temp;
//Export once, if upgrade needed, upgrade and export again
bool export_again = true;
while (export_again) {
export_again = false;
auto ret = keymaster.exportKey(format, kmKey, "!", "!", &key_temp);
if (ret == km::ErrorCode::OK) {
*key = KeyBuffer(key_temp.size());
memcpy(reinterpret_cast<void*>(key->data()), key_temp.c_str(), key->size());
return true;
}
if (ret != km::ErrorCode::KEY_REQUIRES_UPGRADE) return false;
LOG(DEBUG) << "Upgrading key";
std::string kmKeyStr(reinterpret_cast<const char*>(kmKey.data()), kmKey.size());
std::string newKey;
if (!keymaster.upgradeKey(kmKeyStr, km::AuthorizationSet(), &newKey)) return false;
memcpy(reinterpret_cast<void*>(kmKey.data()), newKey.c_str(), kmKey.size());
LOG(INFO) << "Key upgraded";
export_again = true;
}
//Should never come here
return false;
if (!keymaster.exportKey(kmKey, &key_temp)) return false;
*key = KeyBuffer(key_temp.size());
memcpy(reinterpret_cast<void*>(key->data()), key_temp.c_str(), key->size());
return true;
}
static std::pair<km::AuthorizationSet, km::HardwareAuthToken> beginParams(
@@ -212,7 +180,7 @@ static bool readFileToString(const std::string& filename, std::string* result) {
static bool readRandomBytesOrLog(size_t count, std::string* out) {
auto status = ReadRandomBytes(count, *out);
if (status != OK) {
if (status != android::OK) {
LOG(ERROR) << "Random read failed with status: " << status;
return false;
}
@@ -234,27 +202,26 @@ bool readSecdiscardable(const std::string& filename, std::string* hash) {
return true;
}
// static void deferedKmDeleteKey(const std::string& kmkey) {
// while (!android::base::WaitForProperty("vold.checkpoint_committed", "1")) {
// LOG(ERROR) << "Wait for boot timed out";
// }
// Keymaster keymaster;
// if (!keymaster || !keymaster.deleteKey(kmkey)) {
// LOG(ERROR) << "Defered Key deletion failed during upgrade";
// }
// }
static void deferedKmDeleteKey(const std::string& kmkey) {
while (!android::base::WaitForProperty("vold.checkpoint_committed", "1")) {
LOG(ERROR) << "Wait for boot timed out";
}
Keymaster keymaster;
if (!keymaster || !keymaster.deleteKey(kmkey)) {
LOG(ERROR) << "Defered Key deletion failed during upgrade";
}
}
bool kmDeleteKey(Keymaster& keymaster, const std::string& kmKey) {
return true;
// bool needs_cp = cp_needsCheckpoint();
bool needs_cp = cp_needsCheckpoint();
// if (needs_cp) {
// std::thread(deferedKmDeleteKey, kmKey).detach();
// LOG(INFO) << "Deferring Key deletion during upgrade";
// return true;
// } else {
// return keymaster.deleteKey(kmKey);
// }
if (needs_cp) {
std::thread(deferedKmDeleteKey, kmKey).detach();
LOG(INFO) << "Deferring Key deletion during upgrade";
return true;
} else {
return keymaster.deleteKey(kmKey);
}
}
static KeymasterOperation begin(Keymaster& keymaster, const std::string& dir,
@@ -283,7 +250,7 @@ static KeymasterOperation begin(Keymaster& keymaster, const std::string& dir,
// PLOG(ERROR) << "Unable to move upgraded key to location: " << kmKeyPath;
// return KeymasterOperation();
// }
// if (!android::vold::FsyncDirectory(dir)) {
// if (!::FsyncDirectory(dir)) {
// LOG(ERROR) << "Key dir sync failed: " << dir;
// return KeymasterOperation();
// }
@@ -292,7 +259,7 @@ static KeymasterOperation begin(Keymaster& keymaster, const std::string& dir,
// }
// }
kmKey = newKey;
LOG(INFO) << "Key upgraded in memory: " << dir;
LOG(INFO) << "Key upgraded: " << dir;
}
}
@@ -522,7 +489,7 @@ bool storeKey(const std::string& dir, const KeyAuthentication& auth, const KeyBu
if (!writeStringToFile(stretching, dir + "/" + kFn_stretching)) return false;
std::string salt;
if (stretchingNeedsSalt(stretching)) {
if (ReadRandomBytes(SALT_BYTES, salt) != OK) {
if (ReadRandomBytes(SALT_BYTES, salt) != android::OK) {
LOG(ERROR) << "Random read failed";
return false;
}
@@ -561,7 +528,7 @@ bool storeKeyAtomically(const std::string& key_path, const std::string& tmp_path
LOG(DEBUG) << "Already exists, destroying: " << tmp_path;
destroyKey(tmp_path); // May be partially created so ignore errors
}
if (!storeKey(tmp_path, auth, key)) return false;
if (!::storeKey(tmp_path, auth, key)) return false;
if (rename(tmp_path.c_str(), key_path.c_str()) != 0) {
PLOG(ERROR) << "Unable to move new key to location: " << key_path;
return false;
@@ -653,6 +620,3 @@ bool destroyKey(const std::string& dir) {
success &= recursiveDeleteKey(dir);
return success;
}
} // namespace vold
} // namespace android
+6 -15
View File
@@ -17,13 +17,10 @@
#ifndef ANDROID_VOLD_KEYSTORAGE_H
#define ANDROID_VOLD_KEYSTORAGE_H
#include "Keymaster.h"
#include "KeyBuffer.h"
#include <cutils/multiuser.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.
@@ -40,13 +37,6 @@ class KeyAuthentication {
const std::string secret;
};
enum class KeyType {
DE_SYS,
DE_USER,
CE_USER,
ME,
};
extern const KeyAuthentication kEmptyAuthentication;
// Checks if path "path" exists.
@@ -76,9 +66,10 @@ bool retrieveKey(const std::string& dir, const KeyAuthentication& auth, KeyBuffe
bool destroyKey(const std::string& dir);
bool runSecdiscardSingle(const std::string& file);
bool generateWrappedKey(userid_t user_id, KeyType key_type, KeyBuffer* key);
bool getEphemeralWrappedKey(km::KeyFormat format, KeyBuffer& kmKey, KeyBuffer* key);
} // namespace vold
} // namespace android
// Generate wrapped storage key using keymaster. Uses STORAGE_KEY tag in keymaster.
bool generateWrappedStorageKey(KeyBuffer* key);
// Export the per-boot boot wrapped storage key using keymaster.
bool exportWrappedStorageKey(const KeyBuffer& kmKey, KeyBuffer* key);
#endif
+298 -98
View File
@@ -16,32 +16,31 @@
#include "KeyUtil.h"
#include <linux/fs.h>
#include <iomanip>
#include <sstream>
#include <string>
#include <fcntl.h>
#include <linux/fscrypt.h>
#include <openssl/sha.h>
#include <sys/ioctl.h>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <keyutils.h>
#include "FsCrypt.h"
#include <fscrypt_uapi.h>
#include "KeyStorage.h"
#include "Utils.h"
#define MAX_USER_ID 0xFFFFFFFF
using android::hardware::keymaster::V4_0::KeyFormat;
using android::vold::KeyType;
namespace android {
namespace vold {
const KeyGeneration neverGen() {
return KeyGeneration{0, false, false};
}
constexpr int FS_AES_256_XTS_KEY_SIZE = 64;
bool randomKey(KeyBuffer* key) {
*key = KeyBuffer(FS_AES_256_XTS_KEY_SIZE);
static bool randomKey(size_t size, KeyBuffer* key) {
*key = KeyBuffer(size);
if (ReadRandomBytes(key->size(), key->data()) != 0) {
// TODO status_t plays badly with PLOG, fix it.
LOG(ERROR) << "Random read failed";
@@ -50,6 +49,56 @@ bool randomKey(KeyBuffer* key) {
return true;
}
bool generateStorageKey(const KeyGeneration& gen, KeyBuffer* key) {
if (!gen.allow_gen) return false;
if (gen.use_hw_wrapped_key) {
if (gen.keysize != FSCRYPT_MAX_KEY_SIZE) {
LOG(ERROR) << "Cannot generate a wrapped key " << gen.keysize << " bytes long";
return false;
}
return generateWrappedStorageKey(key);
} else {
return randomKey(gen.keysize, key);
}
}
// Return true if the kernel supports the ioctls to add/remove fscrypt keys
// directly to/from the filesystem.
bool isFsKeyringSupported(void) {
static bool initialized = false;
static bool supported;
if (!initialized) {
android::base::unique_fd fd(open("/data", O_RDONLY | O_DIRECTORY | O_CLOEXEC));
// FS_IOC_ADD_ENCRYPTION_KEY with a NULL argument will fail with ENOTTY
// if the ioctl isn't supported. Otherwise it will fail with another
// error code such as EFAULT.
errno = 0;
(void)ioctl(fd, FS_IOC_ADD_ENCRYPTION_KEY, NULL);
if (errno == ENOTTY) {
LOG(INFO) << "Kernel doesn't support FS_IOC_ADD_ENCRYPTION_KEY. Falling back to "
"session keyring";
supported = false;
} else {
if (errno != EFAULT) {
PLOG(WARNING) << "Unexpected error from FS_IOC_ADD_ENCRYPTION_KEY";
}
LOG(INFO) << "Detected support for FS_IOC_ADD_ENCRYPTION_KEY";
supported = true;
android::base::SetProperty("ro.crypto.uses_fs_ioc_add_encryption_key", "true");
}
// There's no need to check for FS_IOC_REMOVE_ENCRYPTION_KEY, since it's
// guaranteed to be available if FS_IOC_ADD_ENCRYPTION_KEY is. There's
// also no need to check for support on external volumes separately from
// /data, since either the kernel supports the ioctls on all
// fscrypt-capable filesystems or it doesn't.
initialized = true;
}
return supported;
}
// Get raw keyref - used to make keyname and to pass to ioctl
static std::string generateKeyRef(const uint8_t* key, int length) {
SHA512_CTX c;
@@ -64,35 +113,39 @@ static std::string generateKeyRef(const uint8_t* key, int length) {
unsigned char key_ref2[SHA512_DIGEST_LENGTH];
SHA512_Final(key_ref2, &c);
static_assert(FS_KEY_DESCRIPTOR_SIZE <= SHA512_DIGEST_LENGTH, "Hash too short for descriptor");
return std::string((char*)key_ref2, FS_KEY_DESCRIPTOR_SIZE);
static_assert(FSCRYPT_KEY_DESCRIPTOR_SIZE <= SHA512_DIGEST_LENGTH,
"Hash too short for descriptor");
return std::string((char*)key_ref2, FSCRYPT_KEY_DESCRIPTOR_SIZE);
}
static bool fillKey(const KeyBuffer& key, fscrypt_key* fs_key) {
if (key.size() != FS_AES_256_XTS_KEY_SIZE) {
if (key.size() != FSCRYPT_MAX_KEY_SIZE) {
LOG(ERROR) << "Wrong size key " << key.size();
return false;
}
static_assert(FS_AES_256_XTS_KEY_SIZE <= sizeof(fs_key->raw), "Key too long!");
fs_key->mode = FS_ENCRYPTION_MODE_AES_256_XTS;
fs_key->size = key.size();
memset(fs_key->raw, 0, sizeof(fs_key->raw));
static_assert(FSCRYPT_MAX_KEY_SIZE == sizeof(fs_key->raw), "Mismatch of max key sizes");
fs_key->mode = 0; // unused by kernel
memcpy(fs_key->raw, key.data(), key.size());
fs_key->size = key.size();
return true;
}
static char const* const NAME_PREFIXES[] = {"ext4", "f2fs", "fscrypt", nullptr};
static std::string keyname(const std::string& prefix, const std::string& raw_ref) {
static std::string keyrefstring(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 std::string buildLegacyKeyName(const std::string& prefix, const std::string& raw_ref) {
return prefix + ":" + keyrefstring(raw_ref);
}
// Get the ID of the keyring we store all fscrypt keys in when the kernel is too
// old to support FS_IOC_ADD_ENCRYPTION_KEY and FS_IOC_REMOVE_ENCRYPTION_KEY.
static bool fscryptKeyring(key_serial_t* device_keyring) {
*device_keyring = keyctl_search(KEY_SPEC_SESSION_KEYRING, "keyring", "fscrypt", 0);
if (*device_keyring == -1) {
@@ -102,44 +155,169 @@ static bool fscryptKeyring(key_serial_t* device_keyring) {
return true;
}
// Install password into global keyring
// Return raw key reference for use in policy
bool installKey(const KeyBuffer& key, std::string* raw_ref) {
// Add an encryption key of type "logon" to the global session keyring.
static bool installKeyLegacy(const KeyBuffer& key, const std::string& raw_ref) {
// Place fscrypt_key into automatically zeroing buffer.
KeyBuffer fsKeyBuffer(sizeof(fscrypt_key));
fscrypt_key& fs_key = *reinterpret_cast<fscrypt_key*>(fsKeyBuffer.data());
if (!fillKey(key, &fs_key)) return false;
if (is_wrapped_key_supported()) {
/* When wrapped key is supported, only the first 32 bytes are
the same per boot. The second 32 bytes can change as the ephemeral
key is different. */
*raw_ref = generateKeyRef(fs_key.raw, (fs_key.size)/2);
} else {
*raw_ref = generateKeyRef(fs_key.raw, fs_key.size);
}
key_serial_t device_keyring;
if (!fscryptKeyring(&device_keyring)) return false;
for (char const* const* name_prefix = NAME_PREFIXES; *name_prefix != nullptr; name_prefix++) {
auto ref = keyname(*name_prefix, *raw_ref);
auto ref = buildLegacyKeyName(*name_prefix, raw_ref);
key_serial_t key_id =
add_key("logon", ref.c_str(), (void*)&fs_key, sizeof(fs_key), device_keyring);
if (key_id == -1) {
PLOG(ERROR) << "Failed to insert key into keyring " << device_keyring;
return false;
}
LOG(DEBUG) << "Added key " << key_id << " (" << ref << ") to keyring " << device_keyring
LOG(INFO) << "Added key " << key_id << " (" << ref << ") to keyring " << device_keyring
<< " in process " << getpid();
}
return true;
}
bool evictKey(const std::string& raw_ref) {
// Installs fscrypt-provisioning key into session level kernel keyring.
// This allows for the given key to be installed back into filesystem keyring.
// For more context see reloadKeyFromSessionKeyring.
static bool installProvisioningKey(const KeyBuffer& key, const std::string& ref,
const fscrypt_key_specifier& key_spec) {
key_serial_t device_keyring;
if (!fscryptKeyring(&device_keyring)) return false;
// Place fscrypt_provisioning_key_payload into automatically zeroing buffer.
KeyBuffer buf(sizeof(fscrypt_provisioning_key_payload) + key.size(), 0);
fscrypt_provisioning_key_payload& provisioning_key =
*reinterpret_cast<fscrypt_provisioning_key_payload*>(buf.data());
memcpy(provisioning_key.raw, key.data(), key.size());
provisioning_key.type = key_spec.type;
key_serial_t key_id = add_key("fscrypt-provisioning", ref.c_str(), (void*)&provisioning_key,
buf.size(), device_keyring);
if (key_id == -1) {
PLOG(ERROR) << "Failed to insert fscrypt-provisioning key for " << ref
<< " into session keyring";
return false;
}
LOG(INFO) << "Added fscrypt-provisioning key for " << ref << " to session keyring";
return true;
}
// Build a struct fscrypt_key_specifier for use in the key management ioctls.
static bool buildKeySpecifier(fscrypt_key_specifier* spec, const EncryptionPolicy& policy) {
switch (policy.options.version) {
case 1:
if (policy.key_raw_ref.size() != FSCRYPT_KEY_DESCRIPTOR_SIZE) {
LOG(ERROR) << "Invalid key specifier size for v1 encryption policy: "
<< policy.key_raw_ref.size();
return false;
}
spec->type = FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR;
memcpy(spec->u.descriptor, policy.key_raw_ref.c_str(), FSCRYPT_KEY_DESCRIPTOR_SIZE);
return true;
case 2:
if (policy.key_raw_ref.size() != FSCRYPT_KEY_IDENTIFIER_SIZE) {
LOG(ERROR) << "Invalid key specifier size for v2 encryption policy: "
<< policy.key_raw_ref.size();
return false;
}
spec->type = FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER;
memcpy(spec->u.identifier, policy.key_raw_ref.c_str(), FSCRYPT_KEY_IDENTIFIER_SIZE);
return true;
default:
LOG(ERROR) << "Invalid encryption policy version: " << policy.options.version;
return false;
}
}
// Installs key into keyring of a filesystem mounted on |mountpoint|.
//
// It's callers responsibility to fill key specifier, and either arg->raw or arg->key_id.
//
// In case arg->key_spec.type equals to FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER
// arg->key_spec.u.identifier will be populated with raw key reference generated
// by kernel.
//
// For documentation on difference between arg->raw and arg->key_id see
// https://www.kernel.org/doc/html/latest/filesystems/fscrypt.html#fs-ioc-add-encryption-key
static bool installFsKeyringKey(const std::string& mountpoint, const EncryptionOptions& options,
fscrypt_add_key_arg* arg) {
if (options.use_hw_wrapped_key) arg->flags |= FSCRYPT_ADD_KEY_FLAG_WRAPPED;
android::base::unique_fd fd(open(mountpoint.c_str(), O_RDONLY | O_DIRECTORY | O_CLOEXEC));
if (fd == -1) {
PLOG(ERROR) << "Failed to open " << mountpoint << " to install key";
return false;
}
if (ioctl(fd, FS_IOC_ADD_ENCRYPTION_KEY, arg) != 0) {
PLOG(ERROR) << "Failed to install fscrypt key to " << mountpoint;
return false;
}
return true;
}
bool installKey(const std::string& mountpoint, const EncryptionOptions& options,
const KeyBuffer& key, EncryptionPolicy* policy) {
policy->options = options;
// Put the fscrypt_add_key_arg in an automatically-zeroing buffer, since we
// have to copy the raw key into it.
KeyBuffer arg_buf(sizeof(struct fscrypt_add_key_arg) + key.size(), 0);
struct fscrypt_add_key_arg* arg = (struct fscrypt_add_key_arg*)arg_buf.data();
// Initialize the "key specifier", which is like a name for the key.
switch (options.version) {
case 1:
// A key for a v1 policy is specified by an arbitrary 8-byte
// "descriptor", which must be provided by userspace. We use the
// first 8 bytes from the double SHA-512 of the key itself.
policy->key_raw_ref = generateKeyRef((const uint8_t*)key.data(), key.size());
if (!isFsKeyringSupported()) {
return installKeyLegacy(key, policy->key_raw_ref);
}
if (!buildKeySpecifier(&arg->key_spec, *policy)) {
return false;
}
break;
case 2:
// A key for a v2 policy is specified by an 16-byte "identifier",
// which is a cryptographic hash of the key itself which the kernel
// computes and returns. Any user-provided value is ignored; we
// just need to set the specifier type to indicate that we're adding
// this type of key.
arg->key_spec.type = FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER;
break;
default:
LOG(ERROR) << "Invalid encryption policy version: " << options.version;
return false;
}
arg->raw_size = key.size();
memcpy(arg->raw, key.data(), key.size());
if (!installFsKeyringKey(mountpoint, options, arg)) return false;
if (arg->key_spec.type == FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER) {
// Retrieve the key identifier that the kernel computed.
policy->key_raw_ref =
std::string((char*)arg->key_spec.u.identifier, FSCRYPT_KEY_IDENTIFIER_SIZE);
}
std::string ref = keyrefstring(policy->key_raw_ref);
LOG(INFO) << "Installed fscrypt key with ref " << ref << " to " << mountpoint;
if (!installProvisioningKey(key, ref, arg->key_spec)) return false;
return true;
}
// Remove an encryption key of type "logon" from the global session keyring.
static bool evictKeyLegacy(const std::string& raw_ref) {
key_serial_t device_keyring;
if (!fscryptKeyring(&device_keyring)) return false;
bool success = true;
for (char const* const* name_prefix = NAME_PREFIXES; *name_prefix != nullptr; name_prefix++) {
auto ref = keyname(*name_prefix, raw_ref);
auto ref = buildLegacyKeyName(*name_prefix, raw_ref);
auto key_serial = keyctl_search(device_keyring, "logon", ref.c_str(), 0);
// Unlink the key from the keyring. Prefer unlinking to revoking or
@@ -150,88 +328,110 @@ bool evictKey(const std::string& raw_ref) {
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;
LOG(ERROR) << "Unlinked key with serial " << key_serial << " ref " << ref;
}
}
return success;
}
bool retrieveAndInstallKey(bool create_if_absent, const KeyAuthentication& key_authentication,
const std::string& key_path, const std::string& tmp_path,
std::string* key_ref, bool wrapped_key_supported) {
KeyBuffer key;
if (pathExists(key_path)) {
LOG(DEBUG) << "Key exists, using: " << key_path;
if (!retrieveKey(key_path, key_authentication, &key)) return false;
} else {
if (!create_if_absent) {
LOG(ERROR) << "No key found in " << key_path;
return false;
}
LOG(INFO) << "Creating new key in " << key_path;
if (wrapped_key_supported) {
if(!generateWrappedKey(MAX_USER_ID, KeyType::DE_SYS, &key)) return false;
} else {
if (!randomKey(&key)) return false;
}
if (!storeKeyAtomically(key_path, tmp_path, key_authentication, key)) return false;
static bool evictProvisioningKey(const std::string& ref) {
key_serial_t device_keyring;
if (!fscryptKeyring(&device_keyring)) {
return false;
}
if (wrapped_key_supported) {
KeyBuffer ephemeral_wrapped_key;
if (!getEphemeralWrappedKey(KeyFormat::RAW, key, &ephemeral_wrapped_key)) {
LOG(ERROR) << "Failed to export key in retrieveAndInstallKey";
return false;
}
key = std::move(ephemeral_wrapped_key);
auto key_serial = keyctl_search(device_keyring, "fscrypt-provisioning", ref.c_str(), 0);
if (key_serial == -1 && errno != ENOKEY) {
PLOG(ERROR) << "Error searching session keyring for fscrypt-provisioning key for " << ref;
return false;
}
if (!installKey(key, key_ref)) {
LOG(ERROR) << "Failed to install key in " << key_path;
if (key_serial != -1 && keyctl_unlink(key_serial, device_keyring) != 0) {
PLOG(ERROR) << "Failed to unlink fscrypt-provisioning key for " << ref
<< " from session keyring";
return false;
}
return true;
}
bool retrieveKey(bool create_if_absent, const std::string& key_path, const std::string& tmp_path,
KeyBuffer* key, bool keepOld) {
LOG(ERROR) << "retreiveKey1";
bool evictKey(const std::string& mountpoint, const EncryptionPolicy& policy) {
if (policy.options.version == 1 && !isFsKeyringSupported()) {
return evictKeyLegacy(policy.key_raw_ref);
}
android::base::unique_fd fd(open(mountpoint.c_str(), O_RDONLY | O_DIRECTORY | O_CLOEXEC));
if (fd == -1) {
PLOG(ERROR) << "Failed to open " << mountpoint << " to evict key";
return false;
}
struct fscrypt_remove_key_arg arg;
memset(&arg, 0, sizeof(arg));
if (!buildKeySpecifier(&arg.key_spec, policy)) {
return false;
}
std::string ref = keyrefstring(policy.key_raw_ref);
if (ioctl(fd, FS_IOC_REMOVE_ENCRYPTION_KEY, &arg) != 0) {
PLOG(ERROR) << "Failed to evict fscrypt key with ref " << ref << " from " << mountpoint;
return false;
}
LOG(ERROR) << "Evicted fscrypt key with ref " << ref << " from " << mountpoint;
if (arg.removal_status_flags & FSCRYPT_KEY_REMOVAL_STATUS_FLAG_OTHER_USERS) {
// Should never happen because keys are only added/removed as root.
LOG(ERROR) << "Unexpected case: key with ref " << ref << " is still added by other users!";
} else if (arg.removal_status_flags & FSCRYPT_KEY_REMOVAL_STATUS_FLAG_FILES_BUSY) {
LOG(ERROR) << "Files still open after removing key with ref " << ref
<< ". These files were not locked!";
}
if (!evictProvisioningKey(ref)) return false;
return true;
}
bool retrieveOrGenerateKey(const std::string& key_path, const std::string& tmp_path,
const KeyAuthentication& key_authentication, const KeyGeneration& gen,
KeyBuffer* key, bool keepOld) {
if (pathExists(key_path)) {
LOG(ERROR) << "Key exists, using: " << key_path;
if (!retrieveKey(key_path, kEmptyAuthentication, key, keepOld)) return false;
if (is_metadata_wrapped_key_supported()) {
KeyBuffer ephemeral_wrapped_key;
if (!getEphemeralWrappedKey(KeyFormat::RAW, *key, &ephemeral_wrapped_key)) {
LOG(ERROR) << "Failed to export key for retrieved key";
return false;
}
*key = std::move(ephemeral_wrapped_key);
}
LOG(INFO) << "Key exists, using: " << key_path;
if (!retrieveKey(key_path, key_authentication, key, keepOld)) return false;
} else {
if (!create_if_absent) {
if (!gen.allow_gen) {
LOG(ERROR) << "No key found in " << key_path;
return false;
}
LOG(ERROR) << "Creating new key in " << key_path;
if (is_metadata_wrapped_key_supported()) {
if(!generateWrappedKey(MAX_USER_ID, KeyType::ME, key)) return false;
} else {
if (!randomKey(key)) return false;
}
LOG(ERROR) << "retrieveKey1";
if (!storeKeyAtomically(key_path, tmp_path,
kEmptyAuthentication, *key)) return false;
if (is_metadata_wrapped_key_supported()) {
KeyBuffer ephemeral_wrapped_key;
if (!getEphemeralWrappedKey(KeyFormat::RAW, *key, &ephemeral_wrapped_key)) {
LOG(ERROR) << "Failed to export key for generated key";
return false;
}
*key = std::move(ephemeral_wrapped_key);
}
LOG(INFO) << "Creating new key in " << key_path;
if (!::generateStorageKey(gen, key)) return false;
if (!storeKeyAtomically(key_path, tmp_path, key_authentication, *key)) return false;
}
return true;
}
} // namespace vold
} // namespace android
bool reloadKeyFromSessionKeyring(const std::string& mountpoint, const EncryptionPolicy& policy) {
key_serial_t device_keyring;
if (!fscryptKeyring(&device_keyring)) {
return false;
}
std::string ref = keyrefstring(policy.key_raw_ref);
auto key_serial = keyctl_search(device_keyring, "fscrypt-provisioning", ref.c_str(), 0);
if (key_serial == -1) {
PLOG(ERROR) << "Failed to find fscrypt-provisioning key for " << ref
<< " in session keyring";
return false;
}
LOG(INFO) << "Installing fscrypt-provisioning key for " << ref << " back into " << mountpoint
<< " fs-keyring";
struct fscrypt_add_key_arg arg;
memset(&arg, 0, sizeof(arg));
if (!buildKeySpecifier(&arg.key_spec, policy)) return false;
arg.key_id = key_serial;
if (!installFsKeyringKey(mountpoint, policy.options, &arg)) return false;
return true;
}
+54 -13
View File
@@ -19,24 +19,65 @@
#include "KeyBuffer.h"
#include "KeyStorage.h"
#include "Keymaster.h"
#include <fscrypt/fscrypt.h>
#include <memory>
#include <string>
namespace android {
namespace vold {
bool randomKey(KeyBuffer* key);
bool installKey(const KeyBuffer& key, std::string* raw_ref);
bool evictKey(const std::string& raw_ref);
bool retrieveAndInstallKey(bool create_if_absent, const KeyAuthentication& key_authentication,
const std::string& key_path, const std::string& tmp_path,
std::string* key_ref, bool wrapped_key_supported);
bool retrieveKey(bool create_if_absent, const std::string& key_path, const std::string& tmp_path,
KeyBuffer* key, bool keepOld = true);
using namespace android::fscrypt;
} // namespace vold
} // namespace android
// Description of how to generate a key when needed.
struct KeyGeneration {
size_t keysize;
bool allow_gen;
bool use_hw_wrapped_key;
};
// Generate a key as specified in KeyGeneration
bool generateStorageKey(const KeyGeneration& gen, KeyBuffer* key);
// Returns a key with allow_gen false so generateStorageKey returns false;
// this is used to indicate to retrieveOrGenerateKey that a key should not
// be generated.
const KeyGeneration neverGen();
bool isFsKeyringSupported(void);
// Install a file-based encryption key to the kernel, for use by encrypted files
// on the specified filesystem using the specified encryption policy version.
//
// For v1 policies, we use FS_IOC_ADD_ENCRYPTION_KEY if the kernel supports it.
// Otherwise we add the key to the global session keyring as a "logon" key.
//
// For v2 policies, we always use FS_IOC_ADD_ENCRYPTION_KEY; it's the only way
// the kernel supports.
//
// If kernel supports FS_IOC_ADD_ENCRYPTION_KEY, also installs key of
// fscrypt-provisioning type to the global session keyring. This makes it
// possible to unmount and then remount mountpoint without losing the file-based
// key.
//
// Returns %true on success, %false on failure. On success also sets *policy
// to the EncryptionPolicy used to refer to this key.
bool installKey(const std::string& mountpoint, const EncryptionOptions& options,
const KeyBuffer& key, EncryptionPolicy* policy);
// Evict a file-based encryption key from the kernel.
//
// This undoes the effect of installKey().
//
// If the kernel doesn't support the filesystem-level keyring, the caller is
// responsible for dropping caches.
bool evictKey(const std::string& mountpoint, const EncryptionPolicy& policy);
bool retrieveOrGenerateKey(const std::string& key_path, const std::string& tmp_path,
const KeyAuthentication& key_authentication, const KeyGeneration& gen,
KeyBuffer* key, bool keepOld = true);
// Re-installs a file-based encryption key of fscrypt-provisioning type from the
// global session keyring back into fs keyring of the mountpoint.
bool reloadKeyFromSessionKeyring(const std::string& mountpoint, const EncryptionPolicy& policy);
#endif
+26 -21
View File
@@ -3,7 +3,7 @@
*
* 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
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
@@ -17,11 +17,9 @@
#include "Keymaster.h"
#include <android-base/logging.h>
#include <keymasterV4_0/authorization_set.h>
#include <keymasterV4_0/keymaster_utils.h>
#include <keymasterV4_1/authorization_set.h>
#include <keymasterV4_1/keymaster_utils.h>
namespace android {
namespace vold {
using ::android::hardware::hidl_string;
using ::android::hardware::hidl_vec;
@@ -138,30 +136,25 @@ bool Keymaster::generateKey(const km::AuthorizationSet& inParams, std::string* k
return true;
}
km::ErrorCode Keymaster::exportKey(km::KeyFormat format, KeyBuffer& kmKey, const std::string& clientId,
const std::string& appData, std::string* key) {
bool Keymaster::exportKey(const KeyBuffer& kmKey, std::string* key) {
auto kmKeyBlob = km::support::blob2hidlVec(std::string(kmKey.data(), kmKey.size()));
auto emptyAssign = NULL;
auto kmClientId = (clientId == "!") ? emptyAssign: km::support::blob2hidlVec(clientId);
auto kmAppData = (appData == "!") ? emptyAssign: km::support::blob2hidlVec(appData);
km::ErrorCode km_error;
auto hidlCb = [&](km::ErrorCode ret, const hidl_vec<uint8_t>& exportedKeyBlob) {
km_error = ret;
if (km_error != km::ErrorCode::OK) return;
if(key)
key->assign(reinterpret_cast<const char*>(&exportedKeyBlob[0]),
exportedKeyBlob.size());
if (key)
key->assign(reinterpret_cast<const char*>(&exportedKeyBlob[0]), exportedKeyBlob.size());
};
auto error = mDevice->exportKey(format, kmKeyBlob, kmClientId, kmAppData, hidlCb);
auto error = mDevice->exportKey(km::KeyFormat::RAW, kmKeyBlob, {}, {}, hidlCb);
if (!error.isOk()) {
LOG(ERROR) << "export_key failed: " << error.description();
return km::ErrorCode::UNKNOWN_ERROR;
return false;
}
if (km_error != km::ErrorCode::OK) {
LOG(ERROR) << "export_key failed, code " << int32_t(km_error);
return km_error;
return false;
}
return km::ErrorCode::OK;
return true;
}
bool Keymaster::deleteKey(const std::string& key) {
@@ -233,10 +226,22 @@ bool Keymaster::isSecure() {
return mDevice->halVersion().securityLevel != km::SecurityLevel::SOFTWARE;
}
} // namespace vold
} // namespace android
using namespace ::android::vold;
void Keymaster::earlyBootEnded() {
auto devices = KmDevice::enumerateAvailableDevices();
for (auto& dev : devices) {
auto error = dev->earlyBootEnded();
if (!error.isOk()) {
LOG(ERROR) << "earlyBootEnded call failed: " << error.description() << " for "
<< dev->halVersion().keymasterName;
}
km::V4_1_ErrorCode km_error = error;
if (km_error != km::V4_1_ErrorCode::OK && km_error != km::V4_1_ErrorCode::UNIMPLEMENTED) {
LOG(ERROR) << "Error reporting early boot ending to keymaster: "
<< static_cast<int32_t>(km_error) << " for "
<< dev->halVersion().keymasterName;
}
}
}
int keymaster_compatibility_cryptfs_scrypt() {
Keymaster dev;
+20 -11
View File
@@ -25,12 +25,21 @@
#include <android-base/macros.h>
#include <keymasterV4_1/Keymaster.h>
#include <keymasterV4_0/authorization_set.h>
#include <keymasterV4_1/authorization_set.h>
namespace android {
namespace vold {
namespace km {
using namespace ::android::hardware::keymaster::V4_1;
// Surprisingly -- to me, at least -- this is totally fine. You can re-define symbols that were
// brought in via a using directive (the "using namespace") above. In general this seems like a
// dangerous thing to rely on, but in this case its implications are simple and straightforward:
// km::ErrorCode refers to the 4.0 ErrorCode, though we pull everything else from 4.1.
using ErrorCode = ::android::hardware::keymaster::V4_0::ErrorCode;
using V4_1_ErrorCode = ::android::hardware::keymaster::V4_1::ErrorCode;
} // namespace km
namespace km = ::android::hardware::keymaster::V4_1;
using KmDevice = km::support::Keymaster;
// C++ wrappers to the Keymaster hidl interface.
@@ -102,9 +111,8 @@ class Keymaster {
explicit operator bool() { return mDevice.get() != nullptr; }
// Generate a key in the keymaster from the given params.
bool generateKey(const km::AuthorizationSet& inParams, std::string* key);
// Export a key from keymaster.
km::ErrorCode exportKey(km::KeyFormat format, KeyBuffer& kmKey, const std::string& clientId,
const std::string& appData, std::string* key);
// Exports a keymaster key with STORAGE_KEY tag wrapped with a per-boot ephemeral key
bool exportKey(const KeyBuffer& kmKey, std::string* key);
// If the keymaster supports it, permanently delete a key.
bool deleteKey(const std::string& key);
// Replace stored key blob in response to KM_ERROR_KEY_REQUIRES_UPGRADE.
@@ -117,15 +125,16 @@ class Keymaster {
km::AuthorizationSet* outParams);
bool isSecure();
// Tell all Keymaster instances that early boot has ended and early boot-only keys can no longer
// be created or used.
static void earlyBootEnded();
private:
std::unique_ptr<KmDevice> mDevice;
android::sp<KmDevice> mDevice;
DISALLOW_COPY_AND_ASSIGN(Keymaster);
static bool hmacKeyGenerated;
};
} // namespace vold
} // namespace android
// FIXME no longer needed now cryptfs is in C++.
/*
+197 -123
View File
@@ -23,21 +23,21 @@
#include <vector>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <linux/dm-ioctl.h>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <cutils/fs.h>
#include <fs_mgr.h>
#include <libdm/dm.h>
#include "Checkpoint.h"
#include "CryptoType.h"
#include "EncryptInplace.h"
#include "KeyStorage.h"
#include "KeyUtil.h"
@@ -45,30 +45,64 @@
#include "Utils.h"
#include "VoldUtil.h"
#define DM_CRYPT_BUF_SIZE 4096
#define TABLE_LOAD_RETRIES 10
#define DEFAULT_KEY_TARGET_TYPE "default-key"
using android::fs_mgr::FstabEntry;
using android::fs_mgr::GetEntryForMountPoint;
using android::fs_mgr::ReadDefaultFstab;
using android::vold::KeyBuffer;
using ::KeyBuffer;
using namespace android::dm;
// Parsed from metadata options
struct CryptoOptions {
struct CryptoType cipher = invalid_crypto_type;
bool use_legacy_options_format = false;
bool set_dun = true; // Non-legacy driver always sets DUN
bool use_hw_wrapped_key = false;
};
static const std::string kDmNameUserdata = "userdata";
static const char* kFn_keymaster_key_blob = "keymaster_key_blob";
static const char* kFn_keymaster_key_blob_upgraded = "keymaster_key_blob_upgraded";
// The first entry in this table is the default crypto type.
constexpr CryptoType supported_crypto_types[] = {aes_256_xts, adiantum};
static_assert(validateSupportedCryptoTypes(64, supported_crypto_types,
array_length(supported_crypto_types)),
"We have a CryptoType which was incompletely constructed.");
constexpr CryptoType legacy_aes_256_xts =
CryptoType().set_config_name("aes-256-xts").set_kernel_name("AES-256-XTS").set_keysize(64);
static_assert(isValidCryptoType(64, legacy_aes_256_xts),
"We have a CryptoType which was incompletely constructed.");
// Returns KeyGeneration suitable for key as described in CryptoOptions
const KeyGeneration makeGen(const CryptoOptions& options) {
return KeyGeneration{options.cipher.get_keysize(), true, options.use_hw_wrapped_key};
}
static bool mount_via_fs_mgr(const char* mount_point, const char* blk_device) {
// We're about to mount data not verified by verified boot. Tell Keymaster instances that early
// boot has ended.
::Keymaster::earlyBootEnded();
// fs_mgr_do_mount runs fsck. Use setexeccon to run trusted
// partitions in the fsck domain.
if (setexeccon(android::vold::sFsckContext)) {
if (setexeccon(::sFsckContext)) {
PLOG(ERROR) << "Failed to setexeccon";
return false;
}
if (!ReadDefaultFstab(&fstab_default)) {
PLOG(ERROR) << "Failed to open default fstab";
return -1;
}
auto mount_rc = fs_mgr_do_mount(&fstab_default, const_cast<char*>(mount_point),
const_cast<char*>(blk_device), nullptr,
false);
::cp_needsCheckpoint(), true);
if (setexeccon(nullptr)) {
PLOG(ERROR) << "Failed to clear setexeccon";
return false;
@@ -77,15 +111,10 @@ static bool mount_via_fs_mgr(const char* mount_point, const char* blk_device) {
LOG(ERROR) << "fs_mgr_do_mount failed with rc " << mount_rc;
return false;
}
LOG(DEBUG) << "Mounted " << mount_point;
LOG(INFO) << "mount_via_fs_mgr::Mounted " << mount_point;
return true;
}
android::fs_mgr::Fstab fstab_default;
namespace android {
namespace vold {
// Note: It is possible to orphan a key if it is removed before deleting
// Update this once keymaster APIs change, and we have a proper commit.
static void commit_key(const std::string& dir) {
@@ -111,20 +140,20 @@ static void commit_key(const std::string& dir) {
LOG(INFO) << "Old Key deleted: " << dir;
}
static bool read_key(const FstabEntry& data_rec, bool create_if_absent, KeyBuffer* key) {
if (data_rec.key_dir.empty()) {
LOG(ERROR) << "Failed to get key_dir";
static bool read_key(const std::string& metadata_key_dir, const KeyGeneration& gen,
KeyBuffer* key) {
if (metadata_key_dir.empty()) {
LOG(ERROR) << "Failed to get metadata_key_dir";
return false;
}
std::string key_dir = data_rec.key_dir;
std::string sKey;
auto dir = key_dir + "/key";
LOG(DEBUG) << "key_dir/key: " << dir;
auto dir = metadata_key_dir + "/key";
LOG(INFO) << "metadata_key_dir/key: " << dir;
if (fs_mkdirs(dir.c_str(), 0700)) {
PLOG(ERROR) << "Creating directories: " << dir;
return false;
}
auto temp = key_dir + "/tmp";
auto temp = metadata_key_dir + "/tmp";
auto newKeyPath = dir + "/" + kFn_keymaster_key_blob_upgraded;
/* If we have a leftover upgraded key, delete it.
* We either failed an update and must return to the old key,
@@ -134,152 +163,170 @@ static bool read_key(const FstabEntry& data_rec, bool create_if_absent, KeyBuffe
Keymaster keymaster;
if (pathExists(newKeyPath)) {
if (!android::base::ReadFileToString(newKeyPath, &sKey))
LOG(ERROR) << "Failed to read old key: " << dir;
LOG(ERROR) << "Failed to read incomplete key: " << dir;
else if (!keymaster.deleteKey(sKey))
LOG(ERROR) << "Old key deletion failed, continuing anyway: " << dir;
LOG(ERROR) << "Incomplete key deletion failed, continuing anyway: " << dir;
else
unlink(newKeyPath.c_str());
}
// bool needs_cp = cp_needsCheckpoint();
bool needs_cp = false;
if (!android::vold::retrieveKey(create_if_absent, dir, temp, key, needs_cp)) return false;
bool needs_cp = cp_needsCheckpoint();
if (!retrieveOrGenerateKey(dir, temp, kEmptyAuthentication, gen, key, needs_cp)) return false;
if (needs_cp && pathExists(newKeyPath)) std::thread(commit_key, dir).detach();
return true;
}
} // namespace vold
} // namespace android
static KeyBuffer default_key_params(const std::string& real_blkdev, const KeyBuffer& key) {
KeyBuffer hex_key;
if (android::vold::StrToHex(key, hex_key) != android::OK) {
LOG(ERROR) << "Failed to turn key to hex";
return KeyBuffer();
}
auto res = KeyBuffer() + "AES-256-XTS " + hex_key + " " + real_blkdev.c_str() + " 0";
return res;
}
static bool get_number_of_sectors(const std::string& real_blkdev, uint64_t* nr_sec) {
if (android::vold::GetBlockDev512Sectors(real_blkdev, nr_sec) != android::OK) {
if (::GetBlockDev512Sectors(real_blkdev, nr_sec) != android::OK) {
PLOG(ERROR) << "Unable to measure size of " << real_blkdev;
return false;
}
return true;
}
static struct dm_ioctl* dm_ioctl_init(char* buffer, size_t buffer_size, const std::string& dm_name) {
if (buffer_size < sizeof(dm_ioctl)) {
LOG(ERROR) << "dm_ioctl buffer too small";
return nullptr;
static bool create_crypto_blk_dev(const std::string& dm_name, const std::string& blk_device,
const KeyBuffer& key, const CryptoOptions& options,
std::string* crypto_blkdev, uint64_t* nr_sec) {
if (!get_number_of_sectors(blk_device, nr_sec)) return false;
// TODO(paulcrowley): don't hardcode that DmTargetDefaultKey uses 4096-byte
// sectors
*nr_sec &= ~7;
KeyBuffer module_key;
if (options.use_hw_wrapped_key) {
if (!exportWrappedStorageKey(key, &module_key)) {
LOG(ERROR) << "Failed to get ephemeral wrapped key";
return false;
}
} else {
module_key = key;
}
memset(buffer, 0, buffer_size);
struct dm_ioctl* io = (struct dm_ioctl*)buffer;
io->data_size = buffer_size;
io->data_start = sizeof(struct dm_ioctl);
io->version[0] = 4;
io->version[1] = 0;
io->version[2] = 0;
io->flags = 0;
dm_name.copy(io->name, sizeof(io->name));
return io;
}
static bool create_crypto_blk_dev(const std::string& dm_name, uint64_t nr_sec,
const std::string& target_type, const KeyBuffer& crypt_params,
std::string* crypto_blkdev) {
android::base::unique_fd dm_fd(
TEMP_FAILURE_RETRY(open("/dev/device-mapper", O_RDWR | O_CLOEXEC, 0)));
if (dm_fd == -1) {
PLOG(ERROR) << "Cannot open device-mapper";
return false;
}
alignas(struct dm_ioctl) char buffer[DM_CRYPT_BUF_SIZE];
auto io = dm_ioctl_init(buffer, sizeof(buffer), dm_name);
if (!io || ioctl(dm_fd.get(), DM_DEV_CREATE, io) != 0) {
PLOG(ERROR) << "Cannot create dm-crypt device " << dm_name;
KeyBuffer hex_key_buffer;
if (::StrToHex(module_key, hex_key_buffer) != android::OK) {
LOG(ERROR) << "Failed to turn key to hex";
return false;
}
std::string hex_key(hex_key_buffer.data(), hex_key_buffer.size());
// Get the device status, in particular, the name of its device file
io = dm_ioctl_init(buffer, sizeof(buffer), dm_name);
if (ioctl(dm_fd.get(), DM_DEV_STATUS, io) != 0) {
PLOG(ERROR) << "Cannot retrieve dm-crypt device status " << dm_name;
return false;
}
*crypto_blkdev = std::string() + "/dev/block/dm-" +
std::to_string((io->dev & 0xff) | ((io->dev >> 12) & 0xfff00));
auto target = std::make_unique<DmTargetDefaultKey>(0, *nr_sec, options.cipher.get_kernel_name(),
hex_key, blk_device, 0);
if (options.use_legacy_options_format) target->SetUseLegacyOptionsFormat();
if (options.set_dun) target->SetSetDun();
if (options.use_hw_wrapped_key) target->SetWrappedKeyV0();
io = dm_ioctl_init(buffer, sizeof(buffer), dm_name);
size_t paramix = io->data_start + sizeof(struct dm_target_spec);
size_t nullix = paramix + crypt_params.size();
size_t endix = (nullix + 1 + 7) & 8; // Add room for \0 and align to 8 byte boundary
if (endix > sizeof(buffer)) {
LOG(ERROR) << "crypt_params too big for DM_CRYPT_BUF_SIZE";
return false;
}
io->target_count = 1;
auto tgt = (struct dm_target_spec*)(buffer + io->data_start);
tgt->status = 0;
tgt->sector_start = 0;
tgt->length = nr_sec;
target_type.copy(tgt->target_type, sizeof(tgt->target_type));
memcpy(buffer + paramix, crypt_params.data(),
std::min(crypt_params.size(), sizeof(buffer) - paramix));
buffer[nullix] = '\0';
tgt->next = endix;
DmTable table;
table.AddTarget(std::move(target));
auto& dm = DeviceMapper::Instance();
for (int i = 0;; i++) {
if (ioctl(dm_fd.get(), DM_TABLE_LOAD, io) == 0) {
if (dm.CreateDevice(dm_name, table)) {
break;
}
if (i + 1 >= TABLE_LOAD_RETRIES) {
PLOG(ERROR) << "DM_TABLE_LOAD ioctl failed";
PLOG(ERROR) << "Could not create default-key device " << dm_name;
return false;
}
PLOG(INFO) << "DM_TABLE_LOAD ioctl failed, retrying";
PLOG(INFO) << "Could not create default-key device, retrying";
usleep(500000);
}
// Resume this device to activate it
io = dm_ioctl_init(buffer, sizeof(buffer), dm_name);
if (ioctl(dm_fd.get(), DM_DEV_SUSPEND, io)) {
PLOG(ERROR) << "Cannot resume dm-crypt device " << dm_name;
if (!dm.GetDmDevicePathByName(dm_name, crypto_blkdev)) {
LOG(ERROR) << "Cannot retrieve default-key device status " << dm_name;
return false;
}
std::stringstream ss;
ss << *crypto_blkdev;
LOG(INFO) << "Created device: " << ss.str();
return true;
}
static const CryptoType& lookup_cipher(const std::string& cipher_name) {
if (cipher_name.empty()) return supported_crypto_types[0];
for (size_t i = 0; i < array_length(supported_crypto_types); i++) {
if (cipher_name == supported_crypto_types[i].get_config_name()) {
return supported_crypto_types[i];
}
}
return invalid_crypto_type;
}
static bool parse_options(const std::string& options_string, CryptoOptions* options) {
auto parts = android::base::Split(options_string, ":");
if (parts.size() < 1 || parts.size() > 2) {
LOG(ERROR) << "Invalid metadata encryption option: " << options_string;
return false;
}
std::string cipher_name = parts[0];
options->cipher = lookup_cipher(cipher_name);
if (options->cipher.get_kernel_name() == nullptr) {
LOG(ERROR) << "No metadata cipher named " << cipher_name << " found";
return false;
}
if (parts.size() == 2) {
if (parts[1] == "wrappedkey_v0") {
options->use_hw_wrapped_key = true;
} else {
LOG(ERROR) << "Invalid metadata encryption flag: " << parts[1];
return false;
}
}
return true;
}
bool fscrypt_mount_metadata_encrypted(const std::string& blk_device, const std::string& mount_point,
bool needs_encrypt) {
LOG(ERROR) << "fscrypt_mount_metadata_encrypted: " << blk_device << " " << mount_point << " " << needs_encrypt;
// auto encrypted_state = android::base::GetProperty("ro.crypto.state", "");
// if (encrypted_state != "") {
// LOG(ERROR) << "fscrypt_enable_crypto got unexpected starting state: " << encrypted_state;
// return false;
// }
LOG(INFO) << "fscrypt_mount_metadata_encrypted: " << mount_point << " " << needs_encrypt;
auto encrypted_state = android::base::GetProperty("ro.crypto.state", "");
if (encrypted_state != "" && encrypted_state != "encrypted") {
LOG(ERROR) << "fscrypt_enable_crypto got unexpected starting state: " << encrypted_state;
return false;
}
if (!ReadDefaultFstab(&fstab_default)) {
PLOG(ERROR) << "Failed to open default fstab";
return -1;
}
auto data_rec = GetEntryForMountPoint(&fstab_default, mount_point);
if (!data_rec) {
LOG(ERROR) << "Failed to get data_rec";
LOG(ERROR) << "Failed to get data_rec for " << mount_point;
return false;
}
KeyBuffer key;
if (!read_key(*data_rec, needs_encrypt, &key)) return false;
uint64_t nr_sec;
if (!get_number_of_sectors(data_rec->blk_device, &nr_sec)) return false;
std::string crypto_blkdev;
if (!create_crypto_blk_dev(kDmNameUserdata, nr_sec, DEFAULT_KEY_TARGET_TYPE,
default_key_params(blk_device, key), &crypto_blkdev))
constexpr unsigned int pre_gki_level = 29;
unsigned int options_format_version = android::base::GetUintProperty<unsigned int>(
"ro.crypto.dm_default_key.options_format.version",
(GetFirstApiLevel() <= pre_gki_level ? 1 : 2));
CryptoOptions options;
if (options_format_version == 1) {
if (!data_rec->metadata_encryption.empty()) {
LOG(ERROR) << "metadata_encryption options cannot be set in legacy mode";
return false;
}
options.cipher = legacy_aes_256_xts;
options.use_legacy_options_format = true;
options.set_dun = android::base::GetBoolProperty("ro.crypto.set_dun", false);
if (!options.set_dun && data_rec->fs_mgr_flags.checkpoint_blk) {
LOG(ERROR)
<< "Block checkpoints and metadata encryption require ro.crypto.set_dun option";
return false;
}
} else if (options_format_version == 2) {
if (!parse_options(data_rec->metadata_encryption, &options)) return false;
} else {
LOG(ERROR) << "Unknown options_format_version: " << options_format_version;
return false;
}
auto gen = needs_encrypt ? makeGen(options) : neverGen();
KeyBuffer key;
if (!read_key(data_rec->metadata_key_dir, gen, &key)) return false;
std::string crypto_blkdev;
uint64_t nr_sec;
if (!create_crypto_blk_dev(kDmNameUserdata, blk_device, key, options, &crypto_blkdev, &nr_sec))
return false;
// FIXME handle the corrupt case
if (needs_encrypt) {
LOG(INFO) << "Beginning inplace encryption, nr_sec: " << nr_sec;
@@ -297,8 +344,35 @@ bool fscrypt_mount_metadata_encrypted(const std::string& blk_device, const std::
LOG(INFO) << "Inplace encryption complete";
}
LOG(ERROR) << "Mounting metadata-encrypted filesystem:" << mount_point;
mount_via_fs_mgr(data_rec->mount_point.c_str(), crypto_blkdev.c_str());
LOG(INFO) << "Mounting metadata-encrypted filesystem:" << mount_point;
mount_via_fs_mgr(mount_point.c_str(), crypto_blkdev.c_str());
android::base::SetProperty("ro.crypto.fs_crypto_blkdev", crypto_blkdev);
// Record that there's at least one fstab entry with metadata encryption
if (!android::base::SetProperty("ro.crypto.metadata.enabled", "true")) {
LOG(WARNING) << "failed to set ro.crypto.metadata.enabled"; // This isn't fatal
}
return true;
}
static bool get_volume_options(CryptoOptions* options) {
return parse_options(android::base::GetProperty("ro.crypto.volume.metadata.encryption", ""),
options);
}
bool defaultkey_volume_keygen(KeyGeneration* gen) {
CryptoOptions options;
if (!get_volume_options(&options)) return false;
*gen = makeGen(options);
return true;
}
bool defaultkey_setup_ext_volume(const std::string& label, const std::string& blk_device,
const KeyBuffer& key, std::string* out_crypto_blkdev) {
LOG(ERROR) << "defaultkey_setup_ext_volume: " << label << " " << blk_device;
CryptoOptions options;
if (!get_volume_options(&options)) return false;
uint64_t nr_sec;
return create_crypto_blk_dev(label, blk_device, key, options, out_crypto_blkdev, &nr_sec);
}
+10
View File
@@ -19,7 +19,17 @@
#include <string>
#include "KeyBuffer.h"
#include "KeyUtil.h"
bool fscrypt_mount_metadata_encrypted(const std::string& block_device,
const std::string& mount_point, bool needs_encrypt);
bool defaultkey_volume_keygen(KeyGeneration* gen);
bool defaultkey_setup_ext_volume(const std::string& label, const std::string& blk_device,
const KeyBuffer& key,
std::string* out_crypto_blkdev);
#endif
+46
View File
@@ -29,6 +29,7 @@
#include <unistd.h>
#include <fstream>
#include <mntent.h>
#include <unordered_set>
#include <android-base/file.h>
@@ -81,6 +82,51 @@ static bool checkSymlink(const std::string& path, const std::string& prefix) {
return false;
}
// TODO: Refactor the code with KillProcessesWithOpenFiles().
int KillProcessesWithMounts(const std::string& prefix, int signal) {
std::unordered_set<pid_t> pids;
auto proc_d = std::unique_ptr<DIR, int (*)(DIR*)>(opendir("/proc"), closedir);
if (!proc_d) {
PLOG(ERROR) << "Failed to open proc";
return -1;
}
struct dirent* proc_de;
while ((proc_de = readdir(proc_d.get())) != nullptr) {
// We only care about valid PIDs
pid_t pid;
if (proc_de->d_type != DT_DIR) continue;
if (!android::base::ParseInt(proc_de->d_name, &pid)) continue;
// Look for references to prefix
std::string mounts_file(StringPrintf("/proc/%d/mounts", pid));
auto fp = std::unique_ptr<FILE, int (*)(FILE*)>(
setmntent(mounts_file.c_str(), "r"), endmntent);
if (!fp) {
PLOG(WARNING) << "Failed to open " << mounts_file;
continue;
}
// Check if obb directory is mounted, and get all packages of mounted app data directory.
mntent* mentry;
while ((mentry = getmntent(fp.get())) != nullptr) {
if (android::base::StartsWith(mentry->mnt_dir, prefix)) {
pids.insert(pid);
break;
}
}
}
if (signal != 0) {
for (const auto& pid : pids) {
LOG(WARNING) << "Killing pid "<< pid << " with signal " << strsignal(signal) <<
" because it has a mount with prefix " << prefix;
kill(pid, signal);
}
}
return pids.size();
}
int KillProcessesWithOpenFiles(const std::string& prefix, int signal) {
std::unordered_set<pid_t> pids;
+1
View File
@@ -21,6 +21,7 @@ namespace android {
namespace vold {
int KillProcessesWithOpenFiles(const std::string& path, int signal);
int KillProcessesWithMounts(const std::string& path, int signal);
} // namespace vold
} // namespace android
+726 -93
View File
File diff suppressed because it is too large Load Diff
+70 -35
View File
@@ -20,6 +20,7 @@
#include "KeyBuffer.h"
#include <android-base/macros.h>
#include <android-base/unique_fd.h>
#include <cutils/multiuser.h>
#include <selinux/selinux.h>
#include <utils/Errors.h>
@@ -30,8 +31,9 @@
struct DIR;
namespace android {
namespace vold {
static const char* kPropFuse = "persist.sys.fuse";
static const char* kVoldAppDataIsolationEnabled = "persist.sys.vold_app_data_isolation_enabled";
static const char* kExternalStorageSdcardfs = "external_storage.sdcardfs.enabled";
/* SELinux contexts used depending on the block device type */
extern security_context_t sBlkidContext;
@@ -42,72 +44,93 @@ extern security_context_t sFsckUntrustedContext;
// TODO remove this with better solution, b/64143519
extern bool sSleepOnUnmount;
status_t CreateDeviceNode(const std::string& path, dev_t dev);
status_t DestroyDeviceNode(const std::string& path);
std::string GetFuseMountPathForUser(userid_t user_id, const std::string& relative_upper_path);
android::status_t CreateDeviceNode(const std::string& path, dev_t dev);
android::status_t DestroyDeviceNode(const std::string& path);
android::status_t AbortFuseConnections();
int SetQuotaInherit(const std::string& path);
int SetQuotaProjectId(const std::string& path, long projectId);
/*
* Creates and sets up an application-specific path on external
* storage with the correct ACL and project ID (if needed).
*
* ONLY for use with app-specific data directories on external storage!
* (eg, /Android/data/com.foo, /Android/obb/com.foo, etc.)
*/
int PrepareAppDirFromRoot(const std::string& path, const std::string& root, int appUid,
bool fixupExisting);
/* fs_prepare_dir wrapper that creates with SELinux context */
status_t PrepareDir(const std::string& path, mode_t mode, uid_t uid, gid_t gid);
android::status_t PrepareDir(const std::string& path, mode_t mode, uid_t uid, gid_t gid);
/* Really unmounts the path, killing active processes along the way */
status_t ForceUnmount(const std::string& path);
android::status_t ForceUnmount(const std::string& path);
/* Kills any processes using given path */
status_t KillProcessesUsingPath(const std::string& path);
android::status_t KillProcessesUsingPath(const std::string& path);
/* Kills any processes using given mount prifix */
android::status_t KillProcessesWithMountPrefix(const std::string& path);
/* Creates bind mount from source to target */
status_t BindMount(const std::string& source, const std::string& target);
android::status_t BindMount(const std::string& source, const std::string& target);
/** Creates a symbolic link to target */
status_t Symlink(const std::string& target, const std::string& linkpath);
android::status_t Symlink(const std::string& target, const std::string& linkpath);
/** Calls unlink(2) at linkpath */
status_t Unlink(const std::string& linkpath);
android::status_t Unlink(const std::string& linkpath);
/** Creates the given directory if it is not already available */
status_t CreateDir(const std::string& dir, mode_t mode);
android::status_t CreateDir(const std::string& dir, mode_t mode);
bool FindValue(const std::string& raw, const std::string& key, std::string* value);
/* Reads filesystem metadata from device at path */
status_t ReadMetadata(const std::string& path, std::string* fsType, std::string* fsUuid,
android::status_t ReadMetadata(const std::string& path, std::string* fsType, std::string* fsUuid,
std::string* fsLabel);
/* Reads filesystem metadata from untrusted device at path */
status_t ReadMetadataUntrusted(const std::string& path, std::string* fsType, std::string* fsUuid,
android::status_t ReadMetadataUntrusted(const std::string& path, std::string* fsType, std::string* fsUuid,
std::string* fsLabel);
/* Returns either WEXITSTATUS() status, or a negative errno */
status_t ForkExecvp(const std::vector<std::string>& args, std::vector<std::string>* output = nullptr,
android::status_t ForkExecvp(const std::vector<std::string>& args, std::vector<std::string>* output = nullptr,
security_context_t context = nullptr);
pid_t ForkExecvpAsync(const std::vector<std::string>& args);
/* Gets block device size in bytes */
status_t GetBlockDevSize(int fd, uint64_t* size);
status_t GetBlockDevSize(const std::string& path, uint64_t* size);
android::status_t GetBlockDevSize(int fd, uint64_t* size);
android::status_t GetBlockDevSize(const std::string& path, uint64_t* size);
/* Gets block device size in 512 byte sectors */
status_t GetBlockDev512Sectors(const std::string& path, uint64_t* nr_sec);
android::status_t GetBlockDev512Sectors(const std::string& path, uint64_t* nr_sec);
status_t ReadRandomBytes(size_t bytes, std::string& out);
status_t ReadRandomBytes(size_t bytes, char* buffer);
status_t GenerateRandomUuid(std::string& out);
android::status_t ReadRandomBytes(size_t bytes, std::string& out);
android::status_t ReadRandomBytes(size_t bytes, char* buffer);
android::status_t GenerateRandomUuid(std::string& out);
/* Converts hex string to raw bytes, ignoring [ :-] */
status_t HexToStr(const std::string& hex, std::string& str);
android::status_t HexToStr(const std::string& hex, std::string& str);
/* Converts raw bytes to hex string */
status_t StrToHex(const std::string& str, std::string& hex);
android::status_t StrToHex(const std::string& str, std::string& hex);
/* Converts raw key bytes to hex string */
status_t StrToHex(const KeyBuffer& str, KeyBuffer& hex);
android::status_t StrToHex(const KeyBuffer& str, KeyBuffer& hex);
/* Normalize given hex string into consistent format */
status_t NormalizeHex(const std::string& in, std::string& out);
android::status_t NormalizeHex(const std::string& in, std::string& out);
uint64_t GetFreeBytes(const std::string& path);
uint64_t GetTreeBytes(const std::string& path);
bool IsFilesystemSupported(const std::string& fsType);
bool IsSdcardfsUsed();
bool IsFuseDaemon(const pid_t pid);
/* Wipes contents of block device at given path */
status_t WipeBlockDevice(const std::string& path);
android::status_t WipeBlockDevice(const std::string& path);
std::string BuildKeyPath(const std::string& partGuid);
@@ -128,26 +151,38 @@ std::string BuildDataUserDePath(const std::string& volumeUuid, userid_t userid);
dev_t GetDevice(const std::string& path);
status_t RestoreconRecursive(const std::string& path);
android::status_t EnsureDirExists(const std::string& path, mode_t mode, uid_t uid, gid_t gid);
android::status_t RestoreconRecursive(const std::string& path);
// TODO: promote to android::base
bool Readlinkat(int dirfd, const std::string& path, std::string* result);
/* Checks if Android is running in QEMU */
bool IsRunningInEmulator();
// Handles dynamic major assignment for virtio-block
bool IsVirtioBlkDevice(unsigned int major);
status_t UnmountTreeWithPrefix(const std::string& prefix);
status_t UnmountTree(const std::string& mountPoint);
android::status_t UnmountTreeWithPrefix(const std::string& prefix);
android::status_t UnmountTree(const std::string& mountPoint);
status_t DeleteDirContentsAndDir(const std::string& pathname);
status_t DeleteDirContents(const std::string& pathname);
android::status_t DeleteDirContentsAndDir(const std::string& pathname);
android::status_t DeleteDirContents(const std::string& pathname);
status_t WaitForFile(const char* filename, std::chrono::nanoseconds timeout);
android::status_t WaitForFile(const char* filename, std::chrono::nanoseconds timeout);
bool FsyncDirectory(const std::string& dirname);
bool writeStringToFile(const std::string& payload, const std::string& filename);
} // namespace vold
} // namespace android
void ConfigureMaxDirtyRatioForFuse(const std::string& fuse_mount, unsigned int max_ratio);
void ConfigureReadAheadForFuse(const std::string& fuse_mount, size_t read_ahead_kb);
android::status_t MountUserFuse(userid_t user_id, const std::string& absolute_lower_path,
const std::string& relative_upper_path, android::base::unique_fd* fuse_fd);
android::status_t UnmountUserFuse(userid_t userId, const std::string& absolute_lower_path,
const std::string& relative_upper_path);
android::status_t PrepareAndroidDirs(const std::string& volumeRoot);
#endif
+1 -2
View File
@@ -17,8 +17,7 @@
#pragma once
#include <fstab/fstab.h>
#include <sys/cdefs.h>
extern android::fs_mgr::Fstab fstab_default;
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
#define DATA_MNT_POINT "/data"
File diff suppressed because it is too large Load Diff
+7 -190
View File
@@ -17,17 +17,7 @@
#ifndef ANDROID_VOLD_CRYPTFS_H
#define ANDROID_VOLD_CRYPTFS_H
/* This structure starts 16,384 bytes before the end of a hardware
* partition that is encrypted, or in a separate partition. It's location
* is specified by a property set in init.<device>.rc.
* The structure allocates 48 bytes for a key, but the real key size is
* specified in the struct. Currently, the code is hardcoded to use 128
* bit keys.
* The fields after salt are only valid in rev 1.1 and later stuctures.
* Obviously, the filesystem does not include the last 16 kbytes
* of the partition if the crypt_mnt_ftr lives at the end of the
* partition.
*/
#include <string>
#include <linux/types.h>
#include <stdbool.h>
@@ -35,172 +25,10 @@
#include <cutils/properties.h>
/* The current cryptfs version */
#define CURRENT_MAJOR_VERSION 1
#define CURRENT_MINOR_VERSION 3
#include "KeyBuffer.h"
#include "KeyUtil.h"
#define CRYPT_FOOTER_OFFSET 0x4000
#define CRYPT_FOOTER_TO_PERSIST_OFFSET 0x1000
#define CRYPT_PERSIST_DATA_SIZE 0x1000
#define MAX_CRYPTO_TYPE_NAME_LEN 64
#define MAX_KEY_LEN 48
#define SALT_LEN 16
#define SCRYPT_LEN 32
/* definitions of flags in the structure below */
#define CRYPT_MNT_KEY_UNENCRYPTED 0x1 /* The key for the partition is not encrypted. */
#define CRYPT_ENCRYPTION_IN_PROGRESS \
0x2 /* Encryption partially completed, \
encrypted_upto valid*/
#define CRYPT_INCONSISTENT_STATE \
0x4 /* Set when starting encryption, clear when \
exit cleanly, either through success or \
correctly marked partial encryption */
#define CRYPT_DATA_CORRUPT \
0x8 /* Set when encryption is fine, but the \
underlying volume is corrupt */
#define CRYPT_FORCE_ENCRYPTION \
0x10 /* Set when it is time to encrypt this \
volume on boot. Everything in this \
structure is set up correctly as \
though device is encrypted except \
that the master key is encrypted with the \
default password. */
#define CRYPT_FORCE_COMPLETE \
0x20 /* Set when the above encryption cycle is \
complete. On next cryptkeeper entry, match \
the password. If it matches fix the master \
key and remove this flag. */
/* Allowed values for type in the structure below */
#define CRYPT_TYPE_PASSWORD \
0 /* master_key is encrypted with a password \
* Must be zero to be compatible with pre-L \
* devices where type is always password.*/
#define CRYPT_TYPE_DEFAULT \
1 /* master_key is encrypted with default \
* password */
#define CRYPT_TYPE_PATTERN 2 /* master_key is encrypted with a pattern */
#define CRYPT_TYPE_PIN 3 /* master_key is encrypted with a pin */
#define CRYPT_TYPE_MAX_TYPE 3 /* type cannot be larger than this value */
#define CRYPT_MNT_MAGIC 0xD0B5B1C4
#define PERSIST_DATA_MAGIC 0xE950CD44
/* Key Derivation Function algorithms */
#define KDF_PBKDF2 1
#define KDF_SCRYPT 2
/* Algorithms 3 & 4 deprecated before shipping outside of google, so removed */
#define KDF_SCRYPT_KEYMASTER 5
/* Maximum allowed keymaster blob size. */
#define KEYMASTER_BLOB_SIZE 2048
/* __le32 and __le16 defined in system/extras/ext4_utils/ext4_utils.h */
#define __le8 unsigned char
#if !defined(SHA256_DIGEST_LENGTH)
#define SHA256_DIGEST_LENGTH 32
#endif
struct crypt_mnt_ftr {
__le32 magic; /* See above */
__le16 major_version;
__le16 minor_version;
__le32 ftr_size; /* in bytes, not including key following */
__le32 flags; /* See above */
__le32 keysize; /* in bytes */
__le32 crypt_type; /* how master_key is encrypted. Must be a
* CRYPT_TYPE_XXX value */
__le64 fs_size; /* Size of the encrypted fs, in 512 byte sectors */
__le32 failed_decrypt_count; /* count of # of failed attempts to decrypt and
mount, set to 0 on successful mount */
unsigned char crypto_type_name[MAX_CRYPTO_TYPE_NAME_LEN]; /* The type of encryption
needed to decrypt this
partition, null terminated */
__le32 spare2; /* ignored */
unsigned char master_key[MAX_KEY_LEN]; /* The encrypted key for decrypting the filesystem */
unsigned char salt[SALT_LEN]; /* The salt used for this encryption */
__le64 persist_data_offset[2]; /* Absolute offset to both copies of crypt_persist_data
* on device with that info, either the footer of the
* real_blkdevice or the metadata partition. */
__le32 persist_data_size; /* The number of bytes allocated to each copy of the
* persistent data table*/
__le8 kdf_type; /* The key derivation function used. */
/* scrypt parameters. See www.tarsnap.com/scrypt/scrypt.pdf */
__le8 N_factor; /* (1 << N) */
__le8 r_factor; /* (1 << r) */
__le8 p_factor; /* (1 << p) */
__le64 encrypted_upto; /* If we are in state CRYPT_ENCRYPTION_IN_PROGRESS and
we have to stop (e.g. power low) this is the last
encrypted 512 byte sector.*/
__le8 hash_first_block[SHA256_DIGEST_LENGTH]; /* When CRYPT_ENCRYPTION_IN_PROGRESS
set, hash of first block, used
to validate before continuing*/
/* key_master key, used to sign the derived key which is then used to generate
* the intermediate key
* This key should be used for no other purposes! We use this key to sign unpadded
* data, which is acceptable but only if the key is not reused elsewhere. */
__le8 keymaster_blob[KEYMASTER_BLOB_SIZE];
__le32 keymaster_blob_size;
/* Store scrypt of salted intermediate key. When decryption fails, we can
check if this matches, and if it does, we know that the problem is with the
drive, and there is no point in asking the user for more passwords.
Note that if any part of this structure is corrupt, this will not match and
we will continue to believe the user entered the wrong password. In that
case the only solution is for the user to enter a password enough times to
force a wipe.
Note also that there is no need to worry about migration. If this data is
wrong, we simply won't recognise a right password, and will continue to
prompt. On the first password change, this value will be populated and
then we will be OK.
*/
unsigned char scrypted_intermediate_key[SCRYPT_LEN];
/* sha of this structure with this element set to zero
Used when encrypting on reboot to validate structure before doing something
fatal
*/
unsigned char sha256[SHA256_DIGEST_LENGTH];
};
/* Persistant data that should be available before decryption.
* Things like airplane mode, locale and timezone are kept
* here and can be retrieved by the CryptKeeper UI to properly
* configure the phone before asking for the password
* This is only valid if the major and minor version above
* is set to 1.1 or higher.
*
* This is a 4K structure. There are 2 copies, and the code alternates
* writing one and then clearing the previous one. The reading
* code reads the first valid copy it finds, based on the magic number.
* The absolute offset to the first of the two copies is kept in rev 1.1
* and higher crypt_mnt_ftr structures.
*/
struct crypt_persist_entry {
char key[PROPERTY_KEY_MAX];
char val[PROPERTY_VALUE_MAX];
};
/* Should be exactly 4K in size */
struct crypt_persist_data {
__le32 persist_magic;
__le32 persist_valid_entries;
__le32 persist_spare[30];
struct crypt_persist_entry persist_entry[0];
};
#define DATA_MNT_POINT "/data"
#define METADATA_MNT_POINT "/metadata"
/* Return values for cryptfs_crypto_complete */
#define CRYPTO_COMPLETE_NOT_ENCRYPTED 1
@@ -210,11 +38,6 @@ struct crypt_persist_data {
#define CRYPTO_COMPLETE_INCONSISTENT (-3)
#define CRYPTO_COMPLETE_CORRUPT (-4)
/* Return values for cryptfs_enable_inplace*() */
#define ENABLE_INPLACE_OK 0
#define ENABLE_INPLACE_ERR_OTHER (-1)
#define ENABLE_INPLACE_ERR_DEV (-2) /* crypto_blkdev issue */
/* Return values for cryptfs_getfield */
#define CRYPTO_GETFIELD_OK 0
#define CRYPTO_GETFIELD_ERROR_NO_FIELD (-1)
@@ -232,11 +55,8 @@ struct crypt_persist_data {
#define PERSIST_DEL_KEY_ERROR_OTHER (-1)
#define PERSIST_DEL_KEY_ERROR_NO_FIELD (-2)
// Exposed for testing only
int match_multi_entry(const char* key, const char* field, unsigned index);
int wait_and_unmount(const char* mountpoint, bool kill);
typedef int (*kdf_func)(const char* passwd, const unsigned char* salt, unsigned char* ikey,
void* params);
int cryptfs_crypto_complete(void);
int cryptfs_check_passwd(const char* pw);
@@ -245,9 +65,8 @@ int cryptfs_restart(void);
int cryptfs_enable(int type, const char* passwd, int no_ui);
int cryptfs_changepw(int type, const char* newpw);
int cryptfs_enable_default(int no_ui);
int cryptfs_setup_ext_volume(const char* label, const char* real_blkdev, const unsigned char* key,
char* out_crypto_blkdev);
int cryptfs_revert_ext_volume(const char* label);
int cryptfs_setup_ext_volume(const char* label, const char* real_blkdev,
const ::KeyBuffer& key, std::string* out_crypto_blkdev);
int cryptfs_getfield(const char* fieldname, char* value, int len);
int cryptfs_setfield(const char* fieldname, const char* value);
int cryptfs_mount_default_encrypted(void);
@@ -255,8 +74,6 @@ int cryptfs_get_password_type(void);
const char* cryptfs_get_password(void);
void cryptfs_clear_password(void);
int cryptfs_isConvertibleToFBE(void);
uint32_t cryptfs_get_keysize();
const char* cryptfs_get_crypto_name();
const KeyGeneration cryptfs_get_keygen();
#endif /* ANDROID_VOLD_CRYPTFS_H */
+5
View File
@@ -0,0 +1,5 @@
#include <map>
// Store main DE/CE policy
extern std::map<userid_t, EncryptionPolicy> s_de_policies;
extern std::map<userid_t, EncryptionPolicy> s_ce_policies;
extern std::string de_key_raw_ref;
+28 -231
View File
@@ -14,8 +14,6 @@
* limitations under the License.
*/
#include "fscrypt/fscrypt.h"
#include <array>
#include <asm/ioctl.h>
@@ -34,44 +32,23 @@
#include <cutils/properties.h>
#include <logwrap/logwrap.h>
#include <utils/misc.h>
#include <fscrypt/fscrypt.h>
#include "fscrypt_policy.h"
static int encryption_mode = FS_ENCRYPTION_MODE_PRIVATE;
bool fscrypt_is_native() {
LOG(ERROR) << "fscrypt_is_native::ro.crypto.type";
char value[PROPERTY_VALUE_MAX];
property_get("ro.crypto.type", value, "none");
return !strcmp(value, "file");
}
static void log_ls(const char* dirname) {
std::array<const char*, 3> argv = {"ls", "-laZ", dirname};
int status = 0;
auto res =
android_fork_execvp(argv.size(), const_cast<char**>(argv.data()), &status, false, true);
if (res != 0) {
PLOG(ERROR) << argv[0] << " " << argv[1] << " " << argv[2] << "failed";
return;
}
if (!WIFEXITED(status)) {
LOG(ERROR) << argv[0] << " " << argv[1] << " " << argv[2]
<< " did not exit normally, status: " << status;
return;
}
if (WEXITSTATUS(status) != 0) {
LOG(ERROR) << argv[0] << " " << argv[1] << " " << argv[2]
<< " returned failure: " << WEXITSTATUS(status);
return;
}
}
extern "C" void policy_to_hex(const uint8_t* policy, char* hex) {
for (size_t i = 0, j = 0; i < FS_KEY_DESCRIPTOR_SIZE; i++) {
hex[j++] = HEX_LOOKUP[(policy[i] & 0xF0) >> 4];
hex[j++] = HEX_LOOKUP[policy[i] & 0x0F];
}
hex[FS_KEY_DESCRIPTOR_SIZE_HEX - 1] = '\0';
extern "C" void bytes_to_hex(const uint8_t *bytes, size_t num_bytes, char *hex) {
for (size_t i = 0; i < num_bytes; i++) {
sprintf(&hex[2 * i], "%02x", bytes[i]);
}
}
static bool is_dir_empty(const char *dirname, bool *is_empty)
@@ -121,153 +98,6 @@ static uint8_t fscrypt_get_policy_flags(int filenames_encryption_mode) {
return FS_POLICY_FLAGS_PAD_16;
}
static bool fscrypt_policy_set(const char *directory, uint8_t *policy,
size_t policy_length,
int contents_encryption_mode,
int filenames_encryption_mode) {
if (policy_length != FS_KEY_DESCRIPTOR_SIZE) {
LOG(ERROR) << "Policy wrong length: " << policy_length;
return false;
}
char policy_hex[FS_KEY_DESCRIPTOR_SIZE_HEX];
policy_to_hex(policy, policy_hex);
int fd = open(directory, O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC);
if (fd == -1) {
PLOG(ERROR) << "Failed to open directory " << directory;
return false;
}
fscrypt_policy fp;
fp.version = 0;
fp.contents_encryption_mode = contents_encryption_mode;
fp.filenames_encryption_mode = filenames_encryption_mode;
fp.flags = fscrypt_get_policy_flags(filenames_encryption_mode);
memcpy(fp.master_key_descriptor, policy, FS_KEY_DESCRIPTOR_SIZE);
if (ioctl(fd, FS_IOC_SET_ENCRYPTION_POLICY, &fp)) {
PLOG(ERROR) << "Failed to set encryption policy for " << directory << " to " << policy_hex
<< " modes " << contents_encryption_mode << "/" << filenames_encryption_mode;
close(fd);
return false;
}
close(fd);
LOG(INFO) << "Policy for " << directory << " set to " << policy_hex
<< " modes " << contents_encryption_mode << "/" << filenames_encryption_mode;
return true;
}
static bool fscrypt_policy_get(const char *directory, uint8_t *policy,
size_t policy_length,
int contents_encryption_mode,
int filenames_encryption_mode) {
if (policy_length != FS_KEY_DESCRIPTOR_SIZE) {
LOG(ERROR) << "Policy wrong length: " << policy_length;
return false;
}
int fd = open(directory, O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC);
if (fd == -1) {
PLOG(ERROR) << "Failed to open directory " << directory;
return false;
}
fscrypt_policy fp;
memset(&fp, 0, sizeof(fscrypt_policy));
if (ioctl(fd, FS_IOC_GET_ENCRYPTION_POLICY, &fp) != 0) {
PLOG(ERROR) << "Failed to get encryption policy for " << directory;
close(fd);
log_ls(directory);
return false;
}
close(fd);
if ((fp.version != 0)
|| (fp.contents_encryption_mode != contents_encryption_mode)
|| (fp.filenames_encryption_mode != filenames_encryption_mode)
|| (fp.flags !=
fscrypt_get_policy_flags(filenames_encryption_mode))) {
LOG(ERROR) << "Failed to find matching encryption policy for " << directory;
return false;
}
memcpy(policy, fp.master_key_descriptor, FS_KEY_DESCRIPTOR_SIZE);
return true;
}
static bool fscrypt_policy_check(const char *directory, uint8_t *policy,
size_t policy_length,
int contents_encryption_mode,
int filenames_encryption_mode) {
if (policy_length != FS_KEY_DESCRIPTOR_SIZE) {
LOG(ERROR) << "Policy wrong length: " << policy_length;
return false;
}
uint8_t existing_policy[FS_KEY_DESCRIPTOR_SIZE];
if (!fscrypt_policy_get(directory, existing_policy, FS_KEY_DESCRIPTOR_SIZE,
contents_encryption_mode,
filenames_encryption_mode)) return false;
char existing_policy_hex[FS_KEY_DESCRIPTOR_SIZE_HEX];
policy_to_hex(existing_policy, existing_policy_hex);
if (memcmp(policy, existing_policy, FS_KEY_DESCRIPTOR_SIZE) != 0) {
char policy_hex[FS_KEY_DESCRIPTOR_SIZE_HEX];
policy_to_hex(policy, policy_hex);
LOG(ERROR) << "Found policy " << existing_policy_hex << " at " << directory
<< " which doesn't match expected value " << policy_hex;
log_ls(directory);
return false;
}
LOG(INFO) << "Found policy " << existing_policy_hex << " at " << directory
<< " which matches expected value";
return true;
}
int fscrypt_policy_ensure(const char *directory, uint8_t *policy,
size_t policy_length,
const char *contents_encryption_mode,
const char *filenames_encryption_mode) {
int contents_mode = 0;
int filenames_mode = 0;
if (!strcmp(contents_encryption_mode, "software") ||
!strcmp(contents_encryption_mode, "aes-256-xts")) {
contents_mode = FS_ENCRYPTION_MODE_AES_256_XTS;
} else if (!strcmp(contents_encryption_mode, "adiantum")) {
contents_mode = FS_ENCRYPTION_MODE_ADIANTUM;
} else if (!strcmp(contents_encryption_mode, "ice")) {
contents_mode = FS_ENCRYPTION_MODE_PRIVATE;
} else {
LOG(ERROR) << "Invalid file contents encryption mode: "
<< contents_encryption_mode;
return -1;
}
if (!strcmp(filenames_encryption_mode, "aes-256-cts")) {
filenames_mode = FS_ENCRYPTION_MODE_AES_256_CTS;
} else if (!strcmp(filenames_encryption_mode, "aes-256-heh")) {
filenames_mode = FS_ENCRYPTION_MODE_AES_256_HEH;
} else if (!strcmp(filenames_encryption_mode, "adiantum")) {
filenames_mode = FS_ENCRYPTION_MODE_ADIANTUM;
} else {
LOG(ERROR) << "Invalid file names encryption mode: "
<< filenames_encryption_mode;
return -1;
}
bool is_empty;
if (!is_dir_empty(directory, &is_empty)) return -1;
if (is_empty) {
if (!fscrypt_policy_set(directory, policy, policy_length,
contents_mode, filenames_mode)) return -1;
} else {
if (!fscrypt_policy_check(directory, policy, policy_length,
contents_mode, filenames_mode)) return -1;
}
return 0;
}
extern "C" bool fscrypt_set_mode() {
const char* mode_file = "/data/unencrypted/mode";
struct stat st;
@@ -292,12 +122,10 @@ extern "C" bool fscrypt_set_mode() {
std::string contents_encryption_mode_string = std::string(contents_encryption_mode);
int pos = contents_encryption_mode_string.find(":");
PLOG(ERROR) << "contents_encryption_mode_string: " << contents_encryption_mode_string.substr(0, pos);
LOG(INFO) << "contents_encryption_mode_string: " << contents_encryption_mode_string.substr(0, pos);
// if (!strcmp(contents_encryption_mode, "software")) {
if (contents_encryption_mode_string.substr(0, pos) == "software") {
encryption_mode = FS_ENCRYPTION_MODE_AES_256_XTS;
// } else if (!strcmp(contents_encryption_mode, "ice")) {
} else if (contents_encryption_mode_string.substr(0, pos) == "ice") {
encryption_mode = FS_ENCRYPTION_MODE_PRIVATE;
} else {
@@ -309,15 +137,11 @@ extern "C" bool fscrypt_set_mode() {
return true;
}
extern "C" void fscrypt_policy_fill_default_struct(fscrypt_encryption_policy *fep) {
fep->version = 0;
fep->contents_encryption_mode = encryption_mode;
fep->filenames_encryption_mode = FS_ENCRYPTION_MODE_AES_256_CTS;
fep->flags = 0;
memset((void*)&fep->master_key_descriptor[0], 0, FS_KEY_DESCRIPTOR_SIZE);
}
extern "C" bool fscrypt_policy_set_struct(const char *directory, const fscrypt_encryption_policy *fep) {
#ifdef USE_FSCRYPT_POLICY_V1
extern "C" bool fscrypt_policy_set_struct(const char *directory, const struct fscrypt_policy_v1 *fep) {
#else
extern "C" bool fscrypt_policy_set_struct(const char *directory, const struct fscrypt_policy_v2 *fep) {
#endif
int fd = open(directory, O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC);
if (fd == -1) {
printf("failed to open %s\n", directory);
@@ -325,7 +149,6 @@ extern "C" bool fscrypt_policy_set_struct(const char *directory, const fscrypt_e
return false;
}
if (ioctl(fd, FS_IOC_SET_ENCRYPTION_POLICY, fep)) {
printf("failed to set policy for '%s'\n", directory);
PLOG(ERROR) << "Failed to set encryption policy for " << directory;
close(fd);
return false;
@@ -334,56 +157,30 @@ extern "C" bool fscrypt_policy_set_struct(const char *directory, const fscrypt_e
return true;
}
extern "C" bool fscrypt_policy_get_struct(const char *directory, fscrypt_encryption_policy *fep) {
int fd = open(directory, O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC);
#ifdef USE_FSCRYPT_POLICY_V1
extern "C" bool fscrypt_policy_get_struct(const char *directory, struct fscrypt_policy_v1 *fep) {
#else
extern "C" bool fscrypt_policy_get_struct(const char *directory, struct fscrypt_policy_v2 *fep) {
#endif
int fd = open(directory, O_DIRECTORY | O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
if (fd == -1) {
printf("Failed to open '%s'\n", directory);
PLOG(ERROR) << "Failed to open directory " << directory;
return false;
}
memset(fep, 0, sizeof(fscrypt_encryption_policy));
if (ioctl(fd, FS_IOC_GET_ENCRYPTION_POLICY, fep) != 0) {
#ifdef USE_FSCRYPT_POLICY_V1
memset(fep, 0, sizeof(fscrypt_policy_v1));
#else
memset(fep, 0, sizeof(fscrypt_policy_v2));
#endif
struct fscrypt_get_policy_ex_arg ex_policy = {0};
ex_policy.policy_size = sizeof(ex_policy.policy);
if (ioctl(fd, FS_IOC_GET_ENCRYPTION_POLICY_EX, &ex_policy) != 0) {
PLOG(ERROR) << "Failed to get encryption policy for " << directory;
close(fd);
return false;
}
printf("fscrypt_policy_get_struct::fep->version::%d\n", fep->version);
memcpy(fep, &ex_policy.policy.v2, sizeof(ex_policy.policy.v2));
close(fd);
return true;
}
extern "C" bool fscrypt_policy_set(const char *directory, uint8_t *policy,
size_t policy_length, int contents_encryption_mode) {
if (contents_encryption_mode == 0)
contents_encryption_mode = encryption_mode;
if (policy_length != FS_KEY_DESCRIPTOR_SIZE) {
printf("policy wrong length\n");
LOG(ERROR) << "Policy wrong length: " << policy_length;
return false;
}
int fd = open(directory, O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC);
if (fd == -1) {
printf("failed to open %s\n", directory);
PLOG(ERROR) << "Failed to open directory " << directory;
return false;
}
fscrypt_encryption_policy fep;
fep.version = 0;
fep.contents_encryption_mode = contents_encryption_mode;
fep.filenames_encryption_mode = FS_ENCRYPTION_MODE_AES_256_CTS;
fep.flags = 0;
memcpy(fep.master_key_descriptor, policy, FS_KEY_DESCRIPTOR_SIZE);
if (ioctl(fd, FS_IOC_SET_ENCRYPTION_POLICY, &fep)) {
printf("failed to set policy for '%s' '%s'\n", directory, policy);
PLOG(ERROR) << "Failed to set encryption policy for " << directory;
close(fd);
return false;
}
close(fd);
char policy_hex[FS_KEY_DESCRIPTOR_SIZE_HEX];
policy_to_hex(policy, policy_hex);
LOG(INFO) << "Policy for " << directory << " set to " << policy_hex;
return true;
}
+22 -14
View File
@@ -25,6 +25,7 @@
__BEGIN_DECLS
#define FS_KEY_DESCRIPTOR_SIZE_HEX (2 * FS_KEY_DESCRIPTOR_SIZE + 1)
#define FSCRYPT_KEY_IDENTIFIER_HEX_SIZE ((2 * FSCRYPT_KEY_IDENTIFIER_SIZE) + 1)
/* modes not supported by upstream kernel, so not in <linux/fs.h> */
#define FS_ENCRYPTION_MODE_AES_256_HEH 126
@@ -42,22 +43,29 @@ __BEGIN_DECLS
#define HEX_LOOKUP "0123456789abcdef"
struct fscrypt_encryption_policy {
uint8_t version;
uint8_t contents_encryption_mode;
uint8_t filenames_encryption_mode;
uint8_t flags;
uint8_t master_key_descriptor[FS_KEY_DESCRIPTOR_SIZE];
} __attribute__((packed));
bool fscrypt_set_mode();
bool lookup_ref_key(const uint8_t *policy, uint8_t* policy_type);
#ifdef USE_FSCRYPT_POLICY_V1
bool lookup_ref_key(struct fscrypt_policy_v1 *fep, uint8_t* policy_type);
#else
bool lookup_ref_key(struct fscrypt_policy_v2 *fep, uint8_t* policy_type);
#endif
bool lookup_ref_tar(const uint8_t *policy_type, uint8_t *policy);
void policy_to_hex(const uint8_t* policy, char* hex);
bool fscrypt_policy_get_struct(const char *directory, struct fscrypt_encryption_policy *fep);
bool fscrypt_policy_set_struct(const char *directory, const struct fscrypt_encryption_policy *fep);
void fscrypt_policy_fill_default_struct(struct fscrypt_encryption_policy *fep);
#ifdef USE_FSCRYPT_POLICY_V1
bool fscrypt_policy_get_struct(const char *directory, struct fscrypt_policy_v1 *fep);
#else
bool fscrypt_policy_get_struct(const char *directory, struct fscrypt_policy_v2 *fep);
#endif
#ifdef USE_FSCRYPT_POLICY_V1
bool fscrypt_policy_set_struct(const char *directory, const struct fscrypt_policy_v1 *fep);
#else
bool fscrypt_policy_set_struct(const char *directory, const struct fscrypt_policy_v2 *fep);
#endif
void bytes_to_hex(const uint8_t *bytes, size_t num_bytes, char *hex);
__END_DECLS
#endif // _FS_CRYPT_H_
+8 -3
View File
@@ -18,16 +18,21 @@
#include <stdlib.h>
#include <string.h>
#include "fscrypt_policy.h"
#include "fscrypt/fscrypt.h"
int main(int argc, char *argv[]) {
if (argc != 2) {
printf("Must specify a path\n");
return -1;
} else {
fscrypt_encryption_policy fep;
#ifdef USE_FSCRYPT_POLICY_V1
fscrypt_policy_v1 fep;
#else
fscrypt_policy_v2 fep;
#endif
if (fscrypt_policy_get_struct(argv[1], &fep)) {
char policy_hex[FS_KEY_DESCRIPTOR_SIZE_HEX];
policy_to_hex(fep.master_key_descriptor, policy_hex);
char policy_hex[FSCRYPT_KEY_IDENTIFIER_HEX_SIZE];
bytes_to_hex(fep.master_key_identifier, FSCRYPT_KEY_IDENTIFIER_SIZE, policy_hex);
printf("%s\n", policy_hex);
} else {
printf("No policy set\n");
+1 -1
View File
@@ -23,7 +23,7 @@ on early-init
on init
export PATH /sbin:/system/bin
export LD_LIBRARY_PATH /system/lib64
export LD_LIBRARY_PATH /system/lib64:/vendor/lib64/hw
export ANDROID_ROOT /system
export ANDROID_DATA /data
+74 -68
View File
@@ -1,68 +1,74 @@
package twrp
import (
"android/soong/android"
"android/soong/cc"
)
func globalFlags(ctx android.BaseContext) []string {
var cflags []string
if getMakeVars(ctx, "AB_OTA_UPDATER") == "true" {
cflags = append(cflags, "-DAB_OTA_UPDATER=1")
}
return cflags
}
func globalSrcs(ctx android.BaseContext) []string {
var srcs []string
if getMakeVars(ctx, "TWRP_CUSTOM_KEYBOARD") != "" {
srcs = append(srcs, getMakeVars(ctx, "TWRP_CUSTOM_KEYBOARD"))
}
return srcs
}
func globalIncludes(ctx android.BaseContext) []string {
var includes []string
if getMakeVars(ctx, "TW_INCLUDE_CRYPTO") != "" {
includes = append(includes, "bootable/recovery/crypto/fscrypt")
}
return includes
}
func libAospRecoveryDefaults(ctx android.LoadHookContext) {
type props struct {
Target struct {
Android struct {
Cflags []string
Enabled *bool
}
}
Cflags []string
Srcs []string
Include_dirs []string
}
p := &props{}
p.Cflags = globalFlags(ctx)
s := globalSrcs(ctx)
p.Srcs = s
i := globalIncludes(ctx)
p.Include_dirs = i
ctx.AppendProperties(p)
}
func init() {
android.RegisterModuleType("libaosprecovery_defaults", libAospRecoveryDefaultsFactory)
}
func libAospRecoveryDefaultsFactory() android.Module {
module := cc.DefaultsFactory()
android.AddLoadHook(module, libAospRecoveryDefaults)
return module
}
package twrp
import (
"android/soong/android"
"android/soong/cc"
)
func globalFlags(ctx android.BaseContext) []string {
var cflags []string
if getMakeVars(ctx, "AB_OTA_UPDATER") == "true" {
cflags = append(cflags, "-DAB_OTA_UPDATER=1")
}
if getMakeVars(ctx, "TW_USE_FSCRYPT_POLICY") == "1" {
cflags = append(cflags, "-DUSE_FSCRYPT_POLICY_V1")
} else {
cflags = append(cflags, "-DUSE_FSCRYPT_POLICY_V2")
}
return cflags
}
func globalSrcs(ctx android.BaseContext) []string {
var srcs []string
if getMakeVars(ctx, "TWRP_CUSTOM_KEYBOARD") != "" {
srcs = append(srcs, getMakeVars(ctx, "TWRP_CUSTOM_KEYBOARD"))
}
return srcs
}
func globalIncludes(ctx android.BaseContext) []string {
var includes []string
if getMakeVars(ctx, "TW_INCLUDE_CRYPTO") != "" {
includes = append(includes, "bootable/recovery/crypto/fscrypt")
}
return includes
}
func libAospRecoveryDefaults(ctx android.LoadHookContext) {
type props struct {
Target struct {
Android struct {
Cflags []string
Enabled *bool
}
}
Cflags []string
Srcs []string
Include_dirs []string
}
p := &props{}
p.Cflags = globalFlags(ctx)
s := globalSrcs(ctx)
p.Srcs = s
i := globalIncludes(ctx)
p.Include_dirs = i
ctx.AppendProperties(p)
}
func init() {
android.RegisterModuleType("libaosprecovery_defaults", libAospRecoveryDefaultsFactory)
}
func libAospRecoveryDefaultsFactory() android.Module {
module := cc.DefaultsFactory()
android.AddLoadHook(module, libAospRecoveryDefaults)
return module
}
+6 -18
View File
@@ -14,15 +14,9 @@ LOCAL_C_INCLUDES += external/libselinux/include
LOCAL_SHARED_LIBRARIES += libselinux
ifeq ($(TW_INCLUDE_CRYPTO_FBE), true)
ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 29; echo $$?),0)
LOCAL_SHARED_LIBRARIES += libtwrpfscrypt
LOCAL_CFLAGS += -DUSE_FSCRYPT
LOCAL_C_INCLUDES += $(LOCAL_PATH)/../crypto/fscrypt
else
LOCAL_SHARED_LIBRARIES += libe4crypt
LOCAL_CFLAGS += -DHAVE_EXT4_CRYPT
LOCAL_C_INCLUDES += $(LOCAL_PATH)/../crypto/ext4crypt
endif
LOCAL_SHARED_LIBRARIES += libtwrpfscrypt
LOCAL_CFLAGS += -DUSE_FSCRYPT
LOCAL_C_INCLUDES += $(LOCAL_PATH)/../crypto/fscrypt
endif
ifeq ($(TW_LIBTAR_DEBUG),true)
@@ -45,15 +39,9 @@ LOCAL_C_INCLUDES += external/libselinux/include
LOCAL_STATIC_LIBRARIES += libselinux
ifeq ($(TW_INCLUDE_CRYPTO_FBE), true)
ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 29; echo $$?),0)
LOCAL_SHARED_LIBRARIES += libtwrpfscrypt
LOCAL_CFLAGS += -DUSE_FSCRYPT
LOCAL_C_INCLUDES += $(LOCAL_PATH)/../crypto/fscrypt
else
LOCAL_SHARED_LIBRARIES += libe4crypt
LOCAL_CFLAGS += -DHAVE_EXT4_CRYPT
LOCAL_C_INCLUDES += $(LOCAL_PATH)/../crypto/ext4crypt
endif
LOCAL_SHARED_LIBRARIES += libtwrpfscrypt
LOCAL_CFLAGS += -DUSE_FSCRYPT
LOCAL_C_INCLUDES += $(LOCAL_PATH)/../crypto/fscrypt
endif
ifeq ($(TW_LIBTAR_DEBUG),true)
+29 -58
View File
@@ -38,16 +38,16 @@
#include <selinux/selinux.h>
#ifdef HAVE_EXT4_CRYPT
#include "ext4crypt_tar.h"
#endif
#ifdef USE_FSCRYPT
#include "fscrypt_policy.h"
#endif
#include "android_utils.h"
#ifdef TW_LIBTAR_DEBUG
#define DEBUG 1
#endif
struct tar_dev
{
dev_t td_dev;
@@ -84,8 +84,8 @@ tar_append_file(TAR *t, const char *realname, const char *savename)
char path[MAXPATHLEN];
#ifdef DEBUG
printf("==> tar_append_file(TAR=0x%lx (\"%s\"), realname=\"%s\", "
"savename=\"%s\")\n", t, t->pathname, realname,
printf("==> tar_append_file(TAR=0x%p (\"%s\"), realname=\"%s\", "
"savename=\"%s\")\n", (void*) t, t->pathname, realname,
(savename ? savename : "[NULL]"));
#endif
@@ -134,45 +134,6 @@ tar_append_file(TAR *t, const char *realname, const char *savename)
}
}
#ifdef HAVE_EXT4_CRYPT
if (TH_ISDIR(t) && t->options & TAR_STORE_EXT4_POL)
{
if (t->th_buf.eep != NULL)
{
free(t->th_buf.eep);
t->th_buf.eep = NULL;
}
t->th_buf.eep = (struct ext4_encryption_policy*)malloc(sizeof(struct ext4_encryption_policy));
if (!t->th_buf.eep) {
printf("malloc ext4_encryption_policy\n");
return -1;
}
if (e4crypt_policy_get_struct(realname, t->th_buf.eep))
{
char tar_policy[EXT4_KEY_DESCRIPTOR_SIZE];
memset(tar_policy, 0, sizeof(tar_policy));
char policy_hex[EXT4_KEY_DESCRIPTOR_SIZE_HEX];
policy_to_hex(t->th_buf.eep->master_key_descriptor, policy_hex);
if (lookup_ref_key(t->th_buf.eep->master_key_descriptor, &tar_policy[0])) {
printf("found policy '%s' - '%s' - '%s'\n", realname, tar_policy, policy_hex);
memcpy(t->th_buf.eep->master_key_descriptor, tar_policy, EXT4_KEY_DESCRIPTOR_SIZE);
} else {
printf("failed to lookup tar policy for '%s' - '%s'\n", realname, policy_hex);
free(t->th_buf.eep);
t->th_buf.eep = NULL;
return -1;
}
}
else
{
// no policy found, but this is not an error as not all dirs will have a policy
free(t->th_buf.eep);
t->th_buf.eep = NULL;
}
}
#endif
#ifdef USE_FSCRYPT
if (TH_ISDIR(t) && t->options & TAR_STORE_FSCRYPT_POL)
{
@@ -181,21 +142,31 @@ tar_append_file(TAR *t, const char *realname, const char *savename)
free(t->th_buf.fep);
t->th_buf.fep = NULL;
}
t->th_buf.fep = (struct fscrypt_encryption_policy*)malloc(sizeof(struct fscrypt_encryption_policy));
#ifdef USE_FSCRYPT_POLICY_V1
t->th_buf.fep = (struct fscrypt_policy_v1 *)malloc(sizeof(struct fscrypt_policy_v1));
#else
t->th_buf.fep = (struct fscrypt_policy_v2 *)malloc(sizeof(struct fscrypt_policy_v2));
#endif
if (!t->th_buf.fep) {
printf("malloc fs_encryption_policy\n");
return -1;
}
if (fscrypt_policy_get_struct(realname, t->th_buf.fep)) {
uint8_t tar_policy[FS_KEY_DESCRIPTOR_SIZE];
uint8_t tar_policy[FSCRYPT_KEY_IDENTIFIER_SIZE];
memset(tar_policy, 0, sizeof(tar_policy));
char policy_hex[FS_KEY_DESCRIPTOR_SIZE_HEX];
policy_to_hex(t->th_buf.fep->master_key_descriptor, policy_hex);
if (lookup_ref_key(t->th_buf.fep->master_key_descriptor, &tar_policy[0])) {
printf("found fscrypt policy '%s' - '%s' - '%s'\n", realname, tar_policy, policy_hex);
memcpy(t->th_buf.fep->master_key_descriptor, tar_policy, FS_KEY_DESCRIPTOR_SIZE);
char policy_hex[FSCRYPT_KEY_IDENTIFIER_HEX_SIZE];
bytes_to_hex(t->th_buf.fep->master_key_identifier, FSCRYPT_KEY_IDENTIFIER_SIZE, policy_hex);
if (lookup_ref_key(t->th_buf.fep, &tar_policy[0])) {
if (strncmp((char *) tar_policy, "0CE0", 4) == 0 || strncmp((char *) tar_policy, "0DE0", 4) == 0
|| strncmp((char *) tar_policy, "0DK", 3) == 0) {
memcpy(t->th_buf.fep->master_key_identifier, tar_policy, FSCRYPT_KEY_IDENTIFIER_SIZE);
printf("found fscrypt policy '%s' - '%s' - '%s'\n", realname, t->th_buf.fep->master_key_identifier, policy_hex);
} else {
printf("failed to match fscrypt tar policy for '%s' - '%s'\n", realname, policy_hex);
free(t->th_buf.fep);
t->th_buf.fep = NULL;
}
} else {
printf("failed to lookup fscrypt tar policy for '%s' - '%s'\n", realname, policy_hex);
free(t->th_buf.fep);
@@ -266,7 +237,7 @@ tar_append_file(TAR *t, const char *realname, const char *savename)
else
{
#ifdef DEBUG
printf("+++ adding hash for device (0x%lx, 0x%lx)...\n",
printf("+++ adding hash for device (0x%x, 0x%x)...\n",
major(s.st_dev), minor(s.st_dev));
#endif
td = (tar_dev_t *)calloc(1, sizeof(tar_dev_t));
@@ -292,9 +263,9 @@ tar_append_file(TAR *t, const char *realname, const char *savename)
else
{
#ifdef DEBUG
printf("+++ adding entry: device (0x%lx,0x%lx), inode %ld "
printf("+++ adding entry: device (0x%d,0x%x), inode %lu"
"(\"%s\")...\n", major(s.st_dev), minor(s.st_dev),
s.st_ino, realname);
(unsigned long) s.st_ino, realname);
#endif
ti = (tar_ino_t *)calloc(1, sizeof(tar_ino_t));
if (ti == NULL)
@@ -332,7 +303,7 @@ tar_append_file(TAR *t, const char *realname, const char *savename)
if (th_write(t) != 0)
{
#ifdef DEBUG
printf("t->fd = %d\n", t->fd);
printf("t->fd = %ld\n", t->fd);
#endif
return -1;
}
@@ -448,7 +419,7 @@ tar_append_file_contents(TAR *t, const char *savename, mode_t mode,
if (th_write(t) != 0)
{
#ifdef DEBUG
fprintf(stderr, "tar_append_file_contents(): could not write header, t->fd = %d\n", t->fd);
fprintf(stderr, "tar_append_file_contents(): could not write header, t->fd = %ld\n", t->fd);
#endif
return -1;
}
+29 -95
View File
@@ -22,10 +22,6 @@
#define DEBUG 1
#endif
#ifdef HAVE_EXT4_CRYPT
#include "ext4crypt_tar.h"
#endif
#ifdef USE_FSCRYPT
#include "fscrypt_policy.h"
#endif
@@ -37,10 +33,6 @@
#define SELINUX_TAG "RHT.security.selinux="
#define SELINUX_TAG_LEN strlen(SELINUX_TAG)
// Used to identify e4crypt_policy in extended ('x')
#define E4CRYPT_TAG "TWRP.security.e4crypt="
#define E4CRYPT_TAG_LEN strlen(E4CRYPT_TAG)
// Used to identify fscrypt_policy in extended ('x')
#define FSCRYPT_TAG "TWRP.security.fscrypt="
#define FSCRYPT_TAG_LEN strlen(FSCRYPT_TAG)
@@ -153,10 +145,6 @@ th_read(TAR *t)
free(t->th_buf.gnu_longlink);
if (t->th_buf.selinux_context != NULL)
free(t->th_buf.selinux_context);
#ifdef HAVE_EXT4_CRYPT
if (t->th_buf.eep != NULL)
free(t->th_buf.eep);
#endif
#ifdef USE_FSCRYPT
if (t->th_buf.fep != NULL)
@@ -362,71 +350,41 @@ th_read(TAR *t)
printf(" th_read(): android user.inode_code_cache xattr detected\n");
#endif
} // end android user.inode_code_cache xattr
#ifdef HAVE_EXT4_CRYPT
start = strstr(buf, E4CRYPT_TAG);
if (start && start+E4CRYPT_TAG_LEN < buf+len)
{
t->th_buf.eep = (struct ext4_encryption_policy*)malloc(sizeof(struct ext4_encryption_policy));
if (!t->th_buf.eep) {
printf("malloc ext4_encryption_policy\n");
return -1;
}
start += E4CRYPT_TAG_LEN;
if (*start == '2')
{
start++;
char *newline_check = start + sizeof(struct ext4_encryption_policy);
if (*newline_check != '\n')
printf("did not find newline char in expected location, continuing anyway...\n");
memcpy(t->th_buf.eep, start, sizeof(struct ext4_encryption_policy));
#ifdef DEBUG
printf(" th_read(): E4Crypt policy v2 detected: %i %i %i %i %s\n",
(int)t->th_buf.eep->version,
(int)t->th_buf.eep->contents_encryption_mode,
(int)t->th_buf.eep->filenames_encryption_mode,
(int)t->th_buf.eep->flags,
t->th_buf.eep->master_key_descriptor);
#endif
}
else
{
e4crypt_policy_fill_default_struct(t->th_buf.eep);
char *end = strchr(start, '\n');
if(!end)
end = strchr(start, '\0');
if(end)
{
strncpy(t->th_buf.eep->master_key_descriptor, start, end-start);
#ifdef DEBUG
printf(" th_read(): E4Crypt policy v1 detected: %s\n", t->th_buf.eep->master_key_descriptor);
#endif
}
}
}
#endif // HAVE_EXT4_CRYPT
#ifdef USE_FSCRYPT
start = strstr(buf, FSCRYPT_TAG);
if (start && start+FSCRYPT_TAG_LEN < buf+len) {
t->th_buf.fep = (struct fscrypt_encryption_policy*)malloc(sizeof(struct fscrypt_encryption_policy));
#ifdef USE_FSCRYPT_POLICY_V1
t->th_buf.fep = (struct fscrypt_policy_v1*)malloc(sizeof(struct fscrypt_policy_v1));
#else
t->th_buf.fep = (struct fscrypt_policy_v2*)malloc(sizeof(struct fscrypt_policy_v2));
#endif
if (!t->th_buf.fep) {
printf("malloc fscrypt_encryption_policy\n");
printf("malloc failed for fscrypt policy\n");
return -1;
}
start += FSCRYPT_TAG_LEN;
if (*start == '0') {
start++;
char *newline_check = start + sizeof(struct fscrypt_encryption_policy);
#ifdef USE_FSCRYPT_POLICY_V1
char *newline_check = start + sizeof(struct fscrypt_policy_v1);
#else
char *newline_check = start + sizeof(struct fscrypt_policy_v2);
#endif
if (*newline_check != '\n')
printf("did not find newline char in expected location, continuing anyway...\n");
memcpy(t->th_buf.fep, start, sizeof(struct fscrypt_encryption_policy));
#ifdef USE_FSCRYPT_POLICY_V1
memcpy(t->th_buf.fep, start, sizeof(struct fscrypt_policy_v1));
#else
memcpy(t->th_buf.fep, start, sizeof(struct fscrypt_policy_v2));
#endif
#ifdef DEBUG
printf(" th_read(): FSCrypt policy v1 detected: %i %i %i %i %s\n",
printf(" th_read(): FSCrypt policy detected: %i %i %i %i %s\n",
(int)t->th_buf.fep->version,
(int)t->th_buf.fep->contents_encryption_mode,
(int)t->th_buf.fep->filenames_encryption_mode,
(int)t->th_buf.fep->flags,
t->th_buf.fep->master_key_descriptor);
t->th_buf.fep->master_key_identifier);
#endif
}
else {
@@ -632,48 +590,20 @@ th_write(TAR *t)
ptr += sz;
}
#ifdef HAVE_EXT4_CRYPT
if((t->options & TAR_STORE_EXT4_POL) && t->th_buf.eep != NULL)
{
#ifdef DEBUG
printf("th_write(): using e4crypt_policy %s\n",
t->th_buf.eep->master_key_descriptor);
#endif
/* setup size - EXT header has format "*size of this whole tag as ascii numbers* *space* *version code* *content* *newline* */
// size newline
sz = E4CRYPT_TAG_LEN + sizeof(struct ext4_encryption_policy) + 1 + 3 + 1;
if(sz >= 100) // another ascci digit for size
++sz;
if (total_sz + sz >= T_BLOCKSIZE)
{
if (th_write_extended(t, &buf[0], total_sz))
return -1;
ptr = buf;
total_sz = sz;
}
else
total_sz += sz;
snprintf(ptr, T_BLOCKSIZE, "%d "E4CRYPT_TAG"2", (int)sz);
memcpy(ptr + sz - sizeof(struct ext4_encryption_policy) - 1, t->th_buf.eep, sizeof(struct ext4_encryption_policy));
char *nlptr = ptr + sz - 1;
*nlptr = '\n';
ptr += sz;
}
#endif
#ifdef USE_FSCRYPT
if((t->options & TAR_STORE_FSCRYPT_POL) && t->th_buf.fep != NULL)
{
#ifdef DEBUG
printf("th_write(): using fscrypt_policy %s\n",
t->th_buf.fep->master_key_descriptor);
t->th_buf.fep->master_key_identifier);
#endif
/* setup size - EXT header has format "*size of this whole tag as ascii numbers* *space* *version code* *content* *newline* */
// size newline
sz = FSCRYPT_TAG_LEN + sizeof(struct fscrypt_encryption_policy) + 1 + 3 + 1;
#ifdef USE_FSCRYPT_POLICY_V1
sz = FSCRYPT_TAG_LEN + sizeof(struct fscrypt_policy_v1) + 1 + 3 + 1;
#else
sz = FSCRYPT_TAG_LEN + sizeof(struct fscrypt_policy_v2) + 1 + 3 + 1;
#endif
if(sz >= 100) // another ascci digit for size
++sz;
@@ -689,7 +619,11 @@ th_write(TAR *t)
total_sz += sz;
snprintf(ptr, T_BLOCKSIZE, "%d "FSCRYPT_TAG"0", (int)sz);
memcpy(ptr + sz - sizeof(struct fscrypt_encryption_policy) - 1, t->th_buf.fep, sizeof(struct fscrypt_encryption_policy));
#ifdef USE_FSCRYPT_POLICY_V1
memcpy(ptr + sz - sizeof(struct fscrypt_policy_v1) - 1, t->th_buf.fep, sizeof(struct fscrypt_policy_v1));
#else
memcpy(ptr + sz - sizeof(struct fscrypt_policy_v2) - 1, t->th_buf.fep, sizeof(struct fscrypt_policy_v2));
#endif
char *nlptr = ptr + sz - 1;
*nlptr = '\n';
ptr += sz;
+15 -35
View File
@@ -35,14 +35,14 @@
#include <selinux/selinux.h>
#ifdef HAVE_EXT4_CRYPT
#include "ext4crypt_tar.h"
#endif
#ifdef USE_FSCRYPT
#include "fscrypt_policy.h"
#endif
#ifdef TW_LIBTAR_DEBUG
#define DEBUG 1
#endif
#include "android_utils.h"
const unsigned long long progress_size = (unsigned long long)(T_BLOCKSIZE);
@@ -554,44 +554,24 @@ tar_extract_dir(TAR *t, const char *realname)
}
}
#ifdef HAVE_EXT4_CRYPT
if(t->th_buf.eep != NULL)
{
#ifdef DEBUG
printf("tar_extract_file(): restoring EXT4 crypt policy %s to dir %s\n", t->th_buf.eep->master_key_descriptor, realname);
#endif
char binary_policy[EXT4_KEY_DESCRIPTOR_SIZE];
if (!lookup_ref_tar(t->th_buf.eep->master_key_descriptor, &binary_policy[0])) {
printf("error looking up proper e4crypt policy for '%s' - %s\n", realname, t->th_buf.eep->master_key_descriptor);
return -1;
}
char policy_hex[EXT4_KEY_DESCRIPTOR_SIZE_HEX];
policy_to_hex(binary_policy, policy_hex);
printf("restoring policy %s > '%s' to '%s'\n", t->th_buf.eep->master_key_descriptor, policy_hex, realname);
memcpy(&t->th_buf.eep->master_key_descriptor, binary_policy, EXT4_KEY_DESCRIPTOR_SIZE);
if (!e4crypt_policy_set_struct(realname, t->th_buf.eep))
{
printf("tar_extract_file(): failed to restore EXT4 crypt policy to dir '%s' '%s'!!!\n", realname, policy_hex);
//return -1; // This may not be an error in some cases, so log and ignore
}
}
#endif
#ifdef USE_FSCRYPT
if(t->th_buf.fep != NULL)
{
char policy_hex[FSCRYPT_KEY_IDENTIFIER_HEX_SIZE];
#ifdef DEBUG
printf("tar_extract_file(): restoring fscrypt policy %s to dir %s\n", t->th_buf.fep->master_key_descriptor, realname);
bytes_to_hex(t->th_buf.fep->master_key_identifier, FSCRYPT_KEY_IDENTIFIER_SIZE, policy_hex);
printf("tar_extract_dir(): restoring fscrypt policy %s to dir %s\n", (char *)policy_hex, realname);
#endif
uint8_t binary_policy[FS_KEY_DESCRIPTOR_SIZE];
if (!lookup_ref_tar(t->th_buf.fep->master_key_descriptor, &binary_policy[0])) {
printf("error looking up proper fscrypt policy for '%s' - %s\n", realname, t->th_buf.fep->master_key_descriptor);
uint8_t binary_policy[FSCRYPT_KEY_IDENTIFIER_SIZE];
memset(&binary_policy, 0, FSCRYPT_KEY_IDENTIFIER_SIZE);
if (!lookup_ref_tar(t->th_buf.fep->master_key_identifier, &binary_policy[0])) {
printf("error looking up fscrypt policy for '%s' - %s\n", realname, t->th_buf.fep->master_key_identifier);
return -1;
}
char policy_hex[FS_KEY_DESCRIPTOR_SIZE_HEX];
policy_to_hex(binary_policy, policy_hex);
printf("restoring policy %s > '%s' to '%s'\n", t->th_buf.fep->master_key_descriptor, policy_hex, realname);
memcpy(&t->th_buf.fep->master_key_descriptor, binary_policy, FS_KEY_DESCRIPTOR_SIZE);
memcpy(&t->th_buf.fep->master_key_identifier, binary_policy, FSCRYPT_KEY_IDENTIFIER_SIZE);
bytes_to_hex(t->th_buf.fep->master_key_identifier, FSCRYPT_KEY_IDENTIFIER_SIZE, policy_hex);
printf("attempting to restore policy: %s\n", policy_hex);
if (!fscrypt_policy_set_struct(realname, t->th_buf.fep))
{
printf("tar_extract_file(): failed to restore fscrypt policy to dir '%s' '%s'!!!\n", realname, policy_hex);
+7 -11
View File
@@ -20,10 +20,6 @@
#include "libtar_listhash.h"
#ifdef HAVE_EXT4_CRYPT
# include "ext4crypt_tar.h"
#endif
#ifdef USE_FSCRYPT
#include "fscrypt_policy.h"
#endif
@@ -72,11 +68,12 @@ struct tar_header
char *gnu_longname;
char *gnu_longlink;
char *selinux_context;
#ifdef HAVE_EXT4_CRYPT
struct ext4_encryption_policy *eep;
#endif
#ifdef USE_FSCRYPT
struct fscrypt_encryption_policy *fep;
#ifdef USE_FSCRYPT_POLICY_V1
struct fscrypt_policy_v1 *fep;
#else
struct fscrypt_policy_v2 *fep;
#endif
#endif
int has_cap_data;
struct vfs_cap_data cap_data;
@@ -127,12 +124,11 @@ TAR;
#define TAR_IGNORE_CRC 64 /* ignore CRC in file header */
#define TAR_STORE_SELINUX 128 /* store selinux context */
#define TAR_USE_NUMERIC_ID 256 /* favor numeric owner over names */
#ifdef HAVE_EXT4_CRYPT
#define TAR_STORE_EXT4_POL 512 /* store ext4 crypto policy */
#endif
#ifdef USE_FSCRYPT
#define TAR_STORE_FSCRYPT_POL 512 /* store fscrypt crypto policy */
#endif
#define TAR_STORE_POSIX_CAP 1024 /* store posix file capabilities */
#define TAR_STORE_ANDROID_USER_XATTR 2048 /* store android user.* xattr */
+2 -9
View File
@@ -24,10 +24,6 @@
# include <string.h>
#endif
#ifdef HAVE_EXT4_CRYPT
#include "ext4crypt_tar.h"
#endif
#ifdef USE_FSCRYPT
#include "fscrypt_policy.h"
#endif
@@ -64,13 +60,10 @@ th_print(TAR *t)
(t->th_buf.gnu_longname ? t->th_buf.gnu_longname : "[NULL]"));
printf(" gnu_longlink = \"%s\"\n",
(t->th_buf.gnu_longlink ? t->th_buf.gnu_longlink : "[NULL]"));
#ifdef HAVE_EXT4_CRYPT
printf(" eep = \"%s\"\n",
(t->th_buf.eep ? t->th_buf.eep->master_key_descriptor : "[NULL]"));
#endif
#ifdef USE_FSCRYPT
printf(" fep = \"%s\"\n",
(t->th_buf.fep ? t->th_buf.fep->master_key_descriptor : (uint8_t*) "[NULL]"));
(t->th_buf.fep ? t->th_buf.fep->master_key_identifier : (uint8_t*) "[NULL]"));
#endif
}
+7 -6
View File
@@ -48,9 +48,7 @@
#include "adbbu/libtwadbbu.hpp"
#ifdef TW_INCLUDE_CRYPTO
#include "crypto/fde/cryptfs.h"
#ifdef TW_INCLUDE_FBE
#include "crypto/ext4crypt/Decrypt.h"
#endif
#include "Decrypt.h"
#else
#define CRYPT_FOOTER_OFFSET 0x4000
#endif
@@ -763,11 +761,13 @@ bool TWPartition::Decrypt_FBE_DE() {
ExcludeAll(Mount_Point + "/misc/gatekeeper");
ExcludeAll(Mount_Point + "/misc/keystore");
ExcludeAll(Mount_Point + "/drm/kek.dat");
ExcludeAll(Mount_Point + "/system_de/0/spblob"); // contains data needed to decrypt pixel 2
ExcludeAll(Mount_Point + "/system/users/0/gatekeeper.password.key");
ExcludeAll(Mount_Point + "/system/users/0/gatekeeper.pattern.key");
ExcludeAll(Mount_Point + "/system_de/0/spblob"); // contains data needed to decrypt synthetic password
// ExcludeAll(Mount_Point + "/system/users/0/gatekeeper.password.key");
// ExcludeAll(Mount_Point + "/system/users/0/gatekeeper.pattern.key");
ExcludeAll(Mount_Point + "/cache");
ExcludeAll(Mount_Point + "/system/users/0");
ExcludeAll(Mount_Point + "/per_boot"); // removed each boot by init
int retry_count = 3;
while (!Decrypt_DE() && --retry_count)
usleep(2000);
@@ -1018,6 +1018,7 @@ void TWPartition::Apply_TW_Flag(const unsigned flag, const char* str, const bool
break;
case TWFLAG_KEYDIRECTORY:
Key_Directory = str;
LOGINFO("setting Key_Directory to: %s\n", Key_Directory.c_str());
break;
case TWFLAG_DM_USE_ORIGINAL_PATH:
Use_Original_Path = true;
+5 -12
View File
@@ -92,12 +92,10 @@ extern "C" {
#include "gui/rapidxml.hpp"
#include "gui/pages.hpp"
#ifdef TW_INCLUDE_FBE
#include "crypto/ext4crypt/Decrypt.h"
#include "Decrypt.h"
#ifdef TW_INCLUDE_FBE_METADATA_DECRYPT
#ifdef USE_FSCRYPT
#include "crypto/fscrypt/MetadataCrypt.h"
#else
#include "crypto/ext4crypt/MetadataCrypt.h"
#include "MetadataCrypt.h"
#endif
#endif
#endif
@@ -404,13 +402,9 @@ void TWPartitionManager::Decrypt_Data() {
#ifdef TW_INCLUDE_FBE_METADATA_DECRYPT
#ifdef USE_FSCRYPT
if (fscrypt_mount_metadata_encrypted(Decrypt_Data->Actual_Block_Device, Decrypt_Data->Mount_Point, false)) {
std::string crypto_blkdev =android::base::GetProperty("ro.crypto.fs_crypto_blkdev", "error");
std::string crypto_blkdev = android::base::GetProperty("ro.crypto.fs_crypto_blkdev", "error");
Decrypt_Data->Decrypted_Block_Device = crypto_blkdev;
LOGINFO("Successfully decrypted metadata encrypted data partition with new block device: '%s'\n", crypto_blkdev.c_str());
#else
if (e4crypt_mount_metadata_encrypted(Decrypt_Data->Mount_Point, false, Decrypt_Data->Key_Directory, Decrypt_Data->Actual_Block_Device, &Decrypt_Data->Decrypted_Block_Device)) {
LOGINFO("Successfully decrypted metadata encrypted data partition with new block device: '%s'\n",
Decrypt_Data->Decrypted_Block_Device.c_str());
#endif
Decrypt_Data->Is_Decrypted = true; // Needed to make the mount function work correctly
int retry_count = 10;
@@ -1768,12 +1762,10 @@ void TWPartitionManager::Post_Decrypt(const string& Block_Device) {
dat->Symlink_Path = dat->Storage_Path;
DataManager::SetValue("tw_storage_path", "/data/media/0");
DataManager::SetValue("tw_settings_path", "/data/media/0");
dat->UnMount(false);
}
DataManager::LoadTWRPFolderInfo();
Update_System_Details();
Output_Partition(dat);
UnMount_Main_Partitions();
} else
LOGERR("Unable to locate data partition.\n");
}
@@ -1794,8 +1786,9 @@ void TWPartitionManager::Parse_Users() {
// Attempt to get name of user. Fallback to user ID if this fails.
char* userFile = PageManager::LoadFileToBuffer("/data/system/users/" + to_string(userId) + ".xml", NULL);
if (userFile == NULL)
if (userFile == NULL) {
user.userName = to_string(userId);
}
else {
xml_document<> *userXml = new xml_document<>();
userXml->parse<0>(userFile);
+3 -1
View File
@@ -26,7 +26,9 @@
#include "exclude.hpp"
#include "tw_atomic.hpp"
#include "progresstracking.hpp"
#include "crypto/fscrypt/fscrypt_policy.h"
#ifdef TW_INCLUDE_CRYPTO
#include "fscrypt_policy.h"
#endif
#include "twrpApex.hpp"
#define MAX_FSTAB_LINE_LENGTH 2048
+8 -4
View File
@@ -50,7 +50,7 @@ RECOVERY_LIBRARY_SOURCE_FILES += $(TARGET_RECOVERY_ROOT_OUT)/system/lib64/libc.s
RECOVERY_LIBRARY_SOURCE_FILES += $(TARGET_RECOVERY_ROOT_OUT)/system/lib64/libdl.so
RECOVERY_LIBRARY_SOURCE_FILES += $(TARGET_RECOVERY_ROOT_OUT)/system/lib64/libm.so
RECOVERY_LIBRARY_SOURCE_FILES += $(TARGET_RECOVERY_ROOT_OUT)/system/lib64/libfs_mgr.so
RECOVERY_LIBRARY_SOURCE_FILES += $(TARGET_RECOVERY_ROOT_OUT)/system/lib64/libfscrypt.so
RECOVERY_LIBRARY_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libfscrypt.so
RECOVERY_LIBRARY_SOURCE_FILES += $(TARGET_RECOVERY_ROOT_OUT)/system/lib64/libgsi.so
RECOVERY_LIBRARY_SOURCE_FILES += $(TARGET_RECOVERY_ROOT_OUT)/system/lib64/libkeyutils.so
RECOVERY_LIBRARY_SOURCE_FILES += $(TARGET_RECOVERY_ROOT_OUT)/system/lib64/liblogwrap.so
@@ -189,6 +189,9 @@ ifeq ($(TW_INCLUDE_CRYPTO), true)
RECOVERY_LIBRARY_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/android.hardware.vibrator@1.0.so
RECOVERY_LIBRARY_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/android.hardware.vibrator@1.1.so
RECOVERY_LIBRARY_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/android.hardware.vibrator@1.2.so
RECOVERY_LIBRARY_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libstatslog.so
RECOVERY_LIBRARY_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libsoft_attestation_cert.so
RECOVERY_LIBRARY_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libxml2.so
ifneq ($(wildcard system/keymaster/keymaster_stl.cpp),)
RECOVERY_LIBRARY_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libkeymaster_portable.so
@@ -206,13 +209,17 @@ ifeq ($(TW_INCLUDE_CRYPTO), true)
endif
RECOVERY_LIBRARY_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libsoftkeymaster.so
RECOVERY_LIBRARY_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/android.hardware.keymaster@4.0.so
RECOVERY_LIBRARY_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/android.hardware.keymaster@4.1.so
RECOVERY_LIBRARY_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libkeymaster4support.so
RECOVERY_LIBRARY_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libkeymaster4_1support.so
RECOVERY_LIBRARY_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libkeystore_aidl.so
RECOVERY_LIBRARY_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libkeystore_parcelables.so
RECOVERY_LIBRARY_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libutilscallstack.so
RECOVERY_LIBRARY_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libdexfile.so
RECOVERY_LIBRARY_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libservices.so
RECOVERY_LIBRARY_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libkeymaster_portable.so
RECOVERY_LIBRARY_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libhwbinder.so
RECOVERY_LIBRARY_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libkeystore-attestation-application-id.so
# lshal can be useful for seeing if you have things like the keymaster working properly, but it isn't needed for TWRP to work
#RECOVERY_BINARY_SOURCE_FILES += $(TARGET_OUT_EXECUTABLES)/lshal
#RECOVERY_LIBRARY_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/liblshal.so
@@ -295,9 +302,6 @@ RECOVERY_LIBRARY_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/android.hidl.tok
ifneq ($(wildcard system/core/libkeyutils/Android.bp),)
RECOVERY_LIBRARY_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libkeyutils.so
endif
ifeq ($(wildcard system/libhidl/transport/HidlTransportUtils.cpp),)
RECOVERY_LIBRARY_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/android.hidl.base@1.0.so
endif
ifeq ($(TARGET_ARCH), arm64)
RECOVERY_LIBRARY_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libclang_rt.ubsan_standalone-aarch64-android.so
endif
+14 -4
View File
@@ -1333,7 +1333,12 @@ void TWFunc::List_Mounts() {
}
}
bool TWFunc::Get_Encryption_Policy(fscrypt_encryption_policy &policy, std::string path) {
#ifdef TW_INCLUDE_CRYPTO
#ifdef USE_FSCRYPT_POLICY_V1
bool TWFunc::Get_Encryption_Policy(struct fscrypt_policy_v1 &policy, std::string path) {
#else
bool TWFunc::Get_Encryption_Policy(struct fscrypt_policy_v2 &policy, std::string path) {
#endif
if (!TWFunc::Path_Exists(path)) {
LOGERR("Unable to find %s to get policy\n", path.c_str());
return false;
@@ -1345,20 +1350,25 @@ bool TWFunc::Get_Encryption_Policy(fscrypt_encryption_policy &policy, std::strin
return true;
}
bool TWFunc::Set_Encryption_Policy(std::string path, const fscrypt_encryption_policy &policy) {
#ifdef USE_FSCRYPT_POLICY_V1
bool TWFunc::Set_Encryption_Policy(std::string path, struct fscrypt_policy_v1 &policy) {
#else
bool TWFunc::Set_Encryption_Policy(std::string path, struct fscrypt_policy_v2 &policy) {
#endif
if (!TWFunc::Path_Exists(path)) {
LOGERR("unable to find %s to set policy\n", path.c_str());
return false;
}
uint8_t binary_policy[FS_KEY_DESCRIPTOR_SIZE];
char policy_hex[FS_KEY_DESCRIPTOR_SIZE_HEX];
policy_to_hex(binary_policy, policy_hex);
char policy_hex[FSCRYPT_KEY_IDENTIFIER_HEX_SIZE];
bytes_to_hex(binary_policy, FS_KEY_DESCRIPTOR_SIZE, policy_hex);
if (!fscrypt_policy_set_struct(path.c_str(), &policy)) {
LOGERR("unable to set policy for path: %s\n", path.c_str());
return false;
}
return true;
}
#endif
string TWFunc::Check_For_TwrpFolder() {
string oldFolder = "";
+9 -2
View File
@@ -115,8 +115,15 @@ public:
static bool Is_TWRP_App_In_System(); // Check if the TWRP app is installed in the system partition
static void checkforapp();
static int Property_Override(string Prop_Name, string Prop_Value); // Override properties (including ro. properties)
static bool Get_Encryption_Policy(fscrypt_encryption_policy &policy, std::string path); // return encryption policy for path
static bool Set_Encryption_Policy(std::string path, const fscrypt_encryption_policy &policy); // set encryption policy for path
#ifdef TW_INCLUDE_CRYPTO
#ifdef USE_FSCRYPT_POLICY_V1
static bool Get_Encryption_Policy(struct fscrypt_policy_v1 &policy, std::string path); // return encryption policy for path
static bool Set_Encryption_Policy(std::string path, struct fscrypt_policy_v1 &policy); // set encryption policy for path
#else
static bool Get_Encryption_Policy(struct fscrypt_policy_v2 &policy, std::string path); // return encryption policy for path
static bool Set_Encryption_Policy(std::string path, struct fscrypt_policy_v2 &policy); // set encryption policy for path
#endif
#endif
static void List_Mounts();
static void Clear_Bootloader_Message();
static string Check_For_TwrpFolder();
+1 -10
View File
@@ -57,18 +57,12 @@ extern "C" {
#ifdef TW_INCLUDE_FBE
#ifdef USE_FSCRYPT
#include "fscrypt_policy.h"
#else
#include "crypto/ext4crypt/ext4crypt_tar.h"
#endif
#endif
#ifdef TW_INCLUDE_FBE
#ifdef USE_FSCRYPT
#define TWTAR_FLAGS TAR_GNU | TAR_STORE_SELINUX | TAR_STORE_POSIX_CAP | TAR_STORE_ANDROID_USER_XATTR | TAR_STORE_FSCRYPT_POL
#else
#define TWTAR_FLAGS TAR_GNU | TAR_STORE_SELINUX | TAR_STORE_POSIX_CAP | TAR_STORE_ANDROID_USER_XATTR | TAR_STORE_EXT4_POL
#endif
#else
#define TWTAR_FLAGS TAR_GNU | TAR_STORE_SELINUX | TAR_STORE_POSIX_CAP | TAR_STORE_ANDROID_USER_XATTR
#endif
@@ -90,12 +84,9 @@ twrpTar::twrpTar(void) {
input_fd = -1;
output_fd = -1;
backup_exclusions = NULL;
#ifdef TW_INCLUDE_FBE
#ifdef USE_FSCRYPT
fscrypt_set_mode();
#else
e4crypt_set_mode();
#endif
#endif
}
+2 -1
View File
@@ -64,7 +64,8 @@ cc_library_static {
include_dirs: [
"bootable/recovery",
"bootable/recovery/install/include",
"bootable/recovery/recovery_utils/include"
"bootable/recovery/recovery_utils/include",
"bootable/recovery/crypto/fscrypt"
],
srcs: [