Support File Based Encryption
Change-Id: Ib688ddd0c32d3999590cacd86b6d9b18eac336e9
This commit is contained in:
@@ -296,6 +296,11 @@ ifeq ($(TW_INCLUDE_CRYPTO), true)
|
||||
LOCAL_CFLAGS += -DTW_INCLUDE_CRYPTO
|
||||
LOCAL_SHARED_LIBRARIES += libcryptfslollipop libgpt_twrp
|
||||
LOCAL_C_INCLUDES += external/boringssl/src/include
|
||||
ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 24; echo $$?),0)
|
||||
TW_INCLUDE_CRYPTO_FBE := true
|
||||
LOCAL_CFLAGS += -DTW_INCLUDE_FBE
|
||||
LOCAL_SHARED_LIBRARIES += libe4crypt
|
||||
endif
|
||||
endif
|
||||
ifeq ($(TW_USE_MODEL_HARDWARE_ID_FOR_DEVICE_ID), true)
|
||||
LOCAL_CFLAGS += -DTW_USE_MODEL_HARDWARE_ID_FOR_DEVICE_ID
|
||||
@@ -669,6 +674,9 @@ endif
|
||||
ifeq ($(TW_INCLUDE_CRYPTO), true)
|
||||
include $(commands_recovery_local_path)/crypto/lollipop/Android.mk
|
||||
include $(commands_recovery_local_path)/crypto/scrypt/Android.mk
|
||||
ifeq ($(TW_INCLUDE_CRYPTO_FBE), true)
|
||||
include $(commands_recovery_local_path)/crypto/ext4crypt/Android.mk
|
||||
endif
|
||||
include $(commands_recovery_local_path)/gpt/Android.mk
|
||||
endif
|
||||
ifeq ($(BUILD_ID), GINGERBREAD)
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
ifeq ($(TW_INCLUDE_CRYPTO), true)
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_MODULE := libe4crypt
|
||||
LOCAL_MODULE_TAGS := eng optional
|
||||
LOCAL_CFLAGS :=
|
||||
LOCAL_SRC_FILES := Decrypt.cpp Ext4Crypt.cpp Keymaster.cpp KeyStorage.cpp ScryptParameters.cpp Utils.cpp HashPassword.cpp
|
||||
LOCAL_SHARED_LIBRARIES := libselinux libc libc++ libext4_utils libsoftkeymaster libbase libcrypto libcutils libkeymaster_messages libhardware libprotobuf-cpp-lite
|
||||
LOCAL_STATIC_LIBRARIES := libscrypt_static
|
||||
LOCAL_C_INCLUDES := system/extras/ext4_utils external/scrypt/lib/crypto system/security/keystore 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
|
||||
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
|
||||
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := twrpfbe
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
|
||||
LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
|
||||
LOCAL_SRC_FILES := main.cpp
|
||||
LOCAL_SHARED_LIBRARIES := libe4crypt
|
||||
#LOCAL_LDFLAGS += -Wl,-dynamic-linker,/sbin/linker64
|
||||
|
||||
include $(BUILD_EXECUTABLE)
|
||||
|
||||
endif
|
||||
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Copyright (C) 2016 The Team Win Recovery Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "Decrypt.h"
|
||||
#include "Ext4Crypt.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "ext4_crypt.h"
|
||||
#include "key_control.h"
|
||||
|
||||
#include <hardware/gatekeeper.h>
|
||||
#include "HashPassword.h"
|
||||
|
||||
#include <android-base/file.h>
|
||||
|
||||
int gatekeeper_device_initialize(gatekeeper_device_t **dev) {
|
||||
int ret;
|
||||
const hw_module_t *mod;
|
||||
ret = hw_get_module_by_class(GATEKEEPER_HARDWARE_MODULE_ID, NULL, &mod);
|
||||
|
||||
if (ret!=0) {
|
||||
printf("failed to get hw module\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = gatekeeper_open(mod, dev);
|
||||
|
||||
if (ret!=0)
|
||||
printf("failed to open gatekeeper\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
int Get_Password_Type(const userid_t user_id, std::string& filename) {
|
||||
std::string path;
|
||||
if (user_id == 0) {
|
||||
path = "/data/system/";
|
||||
} else {
|
||||
char user_id_str[5];
|
||||
sprintf(user_id_str, "%i", user_id);
|
||||
path = "/data/system/users/";
|
||||
path += user_id_str;
|
||||
path += "/";
|
||||
}
|
||||
filename = path + "gatekeeper.password.key";
|
||||
struct stat st;
|
||||
if (stat(filename.c_str(), &st) == 0 && st.st_size > 0)
|
||||
return 1;
|
||||
filename = path + "gatekeeper.pattern.key";
|
||||
if (stat(filename.c_str(), &st) == 0 && st.st_size > 0)
|
||||
return 2;
|
||||
printf("Unable to locate gatekeeper password file '%s'\n", filename.c_str());
|
||||
filename = "";
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool Decrypt_DE() {
|
||||
if (!e4crypt_initialize_global_de()) { // this deals with the overarching device encryption
|
||||
printf("e4crypt_initialize_global_de returned fail\n");
|
||||
return false;
|
||||
}
|
||||
if (!e4crypt_init_user0()) {
|
||||
printf("e4crypt_init_user0 returned fail\n");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Decrypt_User(const userid_t user_id, const std::string& Password) {
|
||||
uint8_t *auth_token;
|
||||
uint32_t auth_token_len;
|
||||
int ret;
|
||||
|
||||
struct stat st;
|
||||
if (user_id > 9999) {
|
||||
printf("user_id is too big\n");
|
||||
return false;
|
||||
}
|
||||
std::string filename;
|
||||
bool Default_Password = (Password == "!");
|
||||
if (Get_Password_Type(user_id, filename) == 0 && !Default_Password) {
|
||||
printf("Unknown password type\n");
|
||||
return false;
|
||||
}
|
||||
int flags = FLAG_STORAGE_DE;
|
||||
if (user_id == 0)
|
||||
flags = FLAG_STORAGE_DE;
|
||||
else
|
||||
flags = FLAG_STORAGE_CE;
|
||||
gatekeeper_device_t *device;
|
||||
ret = gatekeeper_device_initialize(&device);
|
||||
if (Default_Password) {
|
||||
if (!e4crypt_unlock_user_key(user_id, 0, "!", "!")) {
|
||||
printf("e4crypt_unlock_user_key returned fail\n");
|
||||
return false;
|
||||
}
|
||||
if (!e4crypt_prepare_user_storage(nullptr, user_id, 0, flags)) {
|
||||
printf("failed to e4crypt_prepare_user_storage\n");
|
||||
return false;
|
||||
}
|
||||
printf("Decrypted Successfully!\n");
|
||||
return true;
|
||||
}
|
||||
if (ret!=0)
|
||||
return false;
|
||||
printf("password filename is '%s'\n", filename.c_str());
|
||||
if (stat(filename.c_str(), &st) != 0) {
|
||||
printf("error stat'ing key file: %s\n", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
std::string handle;
|
||||
if (!android::base::ReadFileToString(filename, &handle)) {
|
||||
printf("Failed to read '%s'\n", filename.c_str());
|
||||
return false;
|
||||
}
|
||||
bool should_reenroll;
|
||||
ret = device->verify(device, user_id, 0, (const uint8_t *)handle.c_str(), st.st_size,
|
||||
(const uint8_t *)Password.c_str(), (uint32_t)Password.size(), &auth_token, &auth_token_len,
|
||||
&should_reenroll);
|
||||
if (ret !=0) {
|
||||
printf("failed to verify\n");
|
||||
return false;
|
||||
}
|
||||
char token_hex[(auth_token_len*2)+1];
|
||||
token_hex[(auth_token_len*2)] = 0;
|
||||
uint32_t i;
|
||||
for (i=0;i<auth_token_len;i++) {
|
||||
sprintf(&token_hex[2*i], "%02X", auth_token[i]);
|
||||
}
|
||||
// The secret is "Android FBE credential hash" plus appended 0x00 to reach 128 bytes then append the user's password then feed that to sha512sum
|
||||
std::string secret = HashPassword(Password);
|
||||
if (!e4crypt_unlock_user_key(user_id, 0, token_hex, secret.c_str())) {
|
||||
printf("e4crypt_unlock_user_key returned fail\n");
|
||||
return false;
|
||||
}
|
||||
if (!e4crypt_prepare_user_storage(nullptr, user_id, 0, flags)) {
|
||||
printf("failed to e4crypt_prepare_user_storage\n");
|
||||
return false;
|
||||
}
|
||||
printf("Decrypted Successfully!\n");
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
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
|
||||
@@ -0,0 +1,521 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
#include "KeyStorage.h"
|
||||
#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>
|
||||
|
||||
#include "ext4_crypt.h"
|
||||
#include "key_control.h"
|
||||
|
||||
#include <hardware/gatekeeper.h>
|
||||
#include "HashPassword.h"
|
||||
|
||||
#define EMULATED_USES_SELINUX 0
|
||||
#define MANAGE_MISC_DIRS 0
|
||||
|
||||
#include <cutils/fs.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;
|
||||
|
||||
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;
|
||||
|
||||
// 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. 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;
|
||||
}
|
||||
|
||||
static 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 ensure_policy(const std::string& raw_ref, const std::string& path) {
|
||||
LOG(INFO) << "ensure_policy '" << path << "'\n";
|
||||
return true;
|
||||
return access(path.c_str(), F_OK) == 0; // ensure policy will set a policy if one is not set on an empty folder - we don't want to do this in recovery
|
||||
/*if (e4crypt_policy_ensure(path.c_str(), raw_ref.data(), raw_ref.size()) != 0) {
|
||||
LOG(ERROR) << "Failed to set policy on: " << path << "\n";
|
||||
return false;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
// 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;
|
||||
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;
|
||||
}
|
||||
// We can only safely prepare DE storage here, since CE keys are probably
|
||||
// entangled with user credentials. The framework will always prepare CE
|
||||
// storage once CE keys are installed.
|
||||
if (!e4crypt_prepare_user_storage(nullptr, 0, 0, FLAG_STORAGE_DE)) {
|
||||
LOG(ERROR) << "Failed to prepare user 0 storage";
|
||||
return false;
|
||||
}
|
||||
|
||||
// If this is a non-FBE device that recently left an emulated mode,
|
||||
// restore user data directories to known-good state.
|
||||
if (!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, 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");
|
||||
// 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(nullptr, user_id), 0770) ||
|
||||
!emulated_unlock(android::vold::BuildDataUserCePath(nullptr, user_id), 0771)) {
|
||||
LOG(ERROR) << "Failed to unlock user " << user_id;
|
||||
return false;
|
||||
}*/
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool e4crypt_prepare_user_storage(const char* volume_uuid, userid_t user_id, int serial,
|
||||
int flags) {
|
||||
|
||||
if (flags & FLAG_STORAGE_DE) {
|
||||
// DE_sys key
|
||||
auto system_legacy_path = android::vold::BuildDataSystemLegacyPath(user_id);
|
||||
auto misc_legacy_path = android::vold::BuildDataMiscLegacyPath(user_id);
|
||||
auto profiles_de_path = android::vold::BuildDataProfilesDePath(user_id);
|
||||
auto foreign_de_path = android::vold::BuildDataProfilesForeignDexDePath(user_id);
|
||||
|
||||
// DE_n key
|
||||
auto system_de_path = android::vold::BuildDataSystemDePath(user_id);
|
||||
auto misc_de_path = android::vold::BuildDataMiscDePath(user_id);
|
||||
auto user_de_path = android::vold::BuildDataUserDePath(volume_uuid, user_id);
|
||||
|
||||
if (!prepare_dir(system_legacy_path, 0700, AID_SYSTEM, AID_SYSTEM)) return false;
|
||||
#if MANAGE_MISC_DIRS
|
||||
if (!prepare_dir(misc_legacy_path, 0750, multiuser_get_uid(user_id, AID_SYSTEM),
|
||||
multiuser_get_uid(user_id, AID_EVERYBODY))) return false;
|
||||
#endif
|
||||
if (!prepare_dir(profiles_de_path, 0771, AID_SYSTEM, AID_SYSTEM)) return false;
|
||||
if (!prepare_dir(foreign_de_path, 0773, AID_SYSTEM, AID_SYSTEM)) return false;
|
||||
|
||||
if (!prepare_dir(system_de_path, 0770, AID_SYSTEM, AID_SYSTEM)) return false;
|
||||
if (!prepare_dir(misc_de_path, 01771, AID_SYSTEM, AID_MISC)) return false;
|
||||
if (!prepare_dir(user_de_path, 0771, AID_SYSTEM, AID_SYSTEM)) return false;
|
||||
|
||||
// For now, FBE is only supported on internal storage
|
||||
if (e4crypt_is_native() && volume_uuid == nullptr) {
|
||||
std::string de_raw_ref;
|
||||
if (!lookup_key_ref(s_de_key_raw_refs, user_id, &de_raw_ref)) return false;
|
||||
if (!ensure_policy(de_raw_ref, system_de_path)) return false;
|
||||
if (!ensure_policy(de_raw_ref, misc_de_path)) return false;
|
||||
if (!ensure_policy(de_raw_ref, user_de_path)) return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (flags & FLAG_STORAGE_CE) {
|
||||
// CE_n key
|
||||
auto system_ce_path = android::vold::BuildDataSystemCePath(user_id);
|
||||
auto misc_ce_path = android::vold::BuildDataMiscCePath(user_id);
|
||||
auto media_ce_path = android::vold::BuildDataMediaCePath(volume_uuid, user_id);
|
||||
auto user_ce_path = android::vold::BuildDataUserCePath(volume_uuid, user_id);
|
||||
|
||||
if (!prepare_dir(system_ce_path, 0770, AID_SYSTEM, AID_SYSTEM)) return false;
|
||||
if (!prepare_dir(misc_ce_path, 01771, AID_SYSTEM, AID_MISC)) return false;
|
||||
if (!prepare_dir(media_ce_path, 0770, AID_MEDIA_RW, AID_MEDIA_RW)) return false;
|
||||
if (!prepare_dir(user_ce_path, 0771, AID_SYSTEM, AID_SYSTEM)) return false;
|
||||
|
||||
// For now, FBE is only supported on internal storage
|
||||
if (e4crypt_is_native() && volume_uuid == nullptr) {
|
||||
std::string ce_raw_ref;
|
||||
if (!lookup_key_ref(s_ce_key_raw_refs, user_id, &ce_raw_ref)) return false;
|
||||
if (!ensure_policy(ce_raw_ref, system_ce_path)) return false;
|
||||
if (!ensure_policy(ce_raw_ref, misc_ce_path)) return false;
|
||||
if (!ensure_policy(ce_raw_ref, media_ce_path)) return false;
|
||||
if (!ensure_policy(ce_raw_ref, user_ce_path)) return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
#include <cutils/multiuser.h>
|
||||
|
||||
#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);
|
||||
|
||||
__END_DECLS
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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>
|
||||
|
||||
#define PASS_PADDING_SIZE 128
|
||||
#define SHA512_HEX_SIZE SHA512_DIGEST_LENGTH * 2
|
||||
|
||||
std::string HashPassword(const std::string& Password) {
|
||||
size_t size = PASS_PADDING_SIZE + Password.size();
|
||||
unsigned char* buffer = (unsigned char*)calloc(1, size);
|
||||
const char* prefix = "Android FBE credential hash";
|
||||
memcpy((void*)buffer, (void*)prefix, strlen(prefix));
|
||||
unsigned char* ptr = buffer + PASS_PADDING_SIZE;
|
||||
memcpy((void*)ptr, Password.c_str(), Password.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;
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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>
|
||||
|
||||
std::string HashPassword(const std::string& Password);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,349 @@
|
||||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "KeyStorage.h"
|
||||
|
||||
#include "Keymaster.h"
|
||||
#include "ScryptParameters.h"
|
||||
#include "Utils.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <iostream>
|
||||
|
||||
#include <openssl/sha.h>
|
||||
|
||||
#include <android-base/file.h>
|
||||
//#include <android-base/logging.h>
|
||||
|
||||
#include <cutils/properties.h>
|
||||
|
||||
#include <hardware/hw_auth_token.h>
|
||||
|
||||
#include <keymaster/authorization_set.h>
|
||||
|
||||
extern "C" {
|
||||
|
||||
#include "crypto_scrypt.h"
|
||||
}
|
||||
|
||||
#define ERROR 1
|
||||
#define LOG(x) std::cout
|
||||
#define PLOG(x) std::cout
|
||||
|
||||
namespace android {
|
||||
namespace vold {
|
||||
|
||||
const KeyAuthentication kEmptyAuthentication{"", ""};
|
||||
|
||||
static constexpr size_t AES_KEY_BYTES = 32;
|
||||
static constexpr size_t GCM_NONCE_BYTES = 12;
|
||||
static constexpr size_t GCM_MAC_BYTES = 16;
|
||||
static constexpr size_t SALT_BYTES = 1 << 4;
|
||||
static constexpr size_t SECDISCARDABLE_BYTES = 1 << 14;
|
||||
static constexpr size_t STRETCHED_BYTES = 1 << 6;
|
||||
|
||||
static constexpr uint32_t AUTH_TIMEOUT = 30; // Seconds
|
||||
|
||||
static const char* kCurrentVersion = "1";
|
||||
static const char* kRmPath = "/system/bin/rm";
|
||||
static const char* kSecdiscardPath = "/system/bin/secdiscard";
|
||||
static const char* kStretch_none = "none";
|
||||
static const char* kStretch_nopassword = "nopassword";
|
||||
static const std::string kStretchPrefix_scrypt = "scrypt ";
|
||||
static const char* kFn_encrypted_key = "encrypted_key";
|
||||
static const char* kFn_keymaster_key_blob = "keymaster_key_blob";
|
||||
static const char* kFn_salt = "salt";
|
||||
static const char* kFn_secdiscardable = "secdiscardable";
|
||||
static const char* kFn_stretching = "stretching";
|
||||
static const char* kFn_version = "version";
|
||||
|
||||
static bool checkSize(const std::string& kind, size_t actual, size_t expected) {
|
||||
if (actual != expected) {
|
||||
LOG(ERROR) << "Wrong number of bytes in " << kind << ", expected " << expected << " got "
|
||||
<< actual;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static std::string hashSecdiscardable(const std::string& secdiscardable) {
|
||||
SHA512_CTX c;
|
||||
|
||||
SHA512_Init(&c);
|
||||
// Personalise the hashing by introducing a fixed prefix.
|
||||
// Hashing applications should use personalization except when there is a
|
||||
// specific reason not to; see section 4.11 of https://www.schneier.com/skein1.3.pdf
|
||||
std::string secdiscardableHashingPrefix = "Android secdiscardable SHA512";
|
||||
secdiscardableHashingPrefix.resize(SHA512_CBLOCK);
|
||||
SHA512_Update(&c, secdiscardableHashingPrefix.data(), secdiscardableHashingPrefix.size());
|
||||
SHA512_Update(&c, secdiscardable.data(), secdiscardable.size());
|
||||
std::string res(SHA512_DIGEST_LENGTH, '\0');
|
||||
SHA512_Final(reinterpret_cast<uint8_t*>(&res[0]), &c);
|
||||
return res;
|
||||
}
|
||||
|
||||
/*static bool generateKeymasterKey(Keymaster& keymaster, const KeyAuthentication& auth,
|
||||
const std::string& appId, std::string* key) {
|
||||
auto paramBuilder = keymaster::AuthorizationSetBuilder()
|
||||
.AesEncryptionKey(AES_KEY_BYTES * 8)
|
||||
.Authorization(keymaster::TAG_BLOCK_MODE, KM_MODE_GCM)
|
||||
.Authorization(keymaster::TAG_MIN_MAC_LENGTH, GCM_MAC_BYTES * 8)
|
||||
.Authorization(keymaster::TAG_PADDING, KM_PAD_NONE);
|
||||
addStringParam(¶mBuilder, keymaster::TAG_APPLICATION_ID, appId);
|
||||
if (auth.token.empty()) {
|
||||
LOG(DEBUG) << "Creating key that doesn't need auth token";
|
||||
paramBuilder.Authorization(keymaster::TAG_NO_AUTH_REQUIRED);
|
||||
} else {
|
||||
LOG(DEBUG) << "Auth token required for key";
|
||||
if (auth.token.size() != sizeof(hw_auth_token_t)) {
|
||||
LOG(ERROR) << "Auth token should be " << sizeof(hw_auth_token_t) << " bytes, was "
|
||||
<< auth.token.size() << " bytes";
|
||||
return false;
|
||||
}
|
||||
const hw_auth_token_t* at = reinterpret_cast<const hw_auth_token_t*>(auth.token.data());
|
||||
paramBuilder.Authorization(keymaster::TAG_USER_SECURE_ID, at->user_id);
|
||||
paramBuilder.Authorization(keymaster::TAG_USER_AUTH_TYPE, HW_AUTH_PASSWORD);
|
||||
paramBuilder.Authorization(keymaster::TAG_AUTH_TIMEOUT, AUTH_TIMEOUT);
|
||||
}
|
||||
return keymaster.generateKey(paramBuilder.build(), key);
|
||||
}*/
|
||||
|
||||
static keymaster::AuthorizationSetBuilder beginParams(const KeyAuthentication& auth,
|
||||
const std::string& appId) {
|
||||
auto paramBuilder = keymaster::AuthorizationSetBuilder()
|
||||
.Authorization(keymaster::TAG_BLOCK_MODE, KM_MODE_GCM)
|
||||
.Authorization(keymaster::TAG_MAC_LENGTH, GCM_MAC_BYTES * 8)
|
||||
.Authorization(keymaster::TAG_PADDING, KM_PAD_NONE);
|
||||
addStringParam(¶mBuilder, keymaster::TAG_APPLICATION_ID, appId);
|
||||
if (!auth.token.empty()) {
|
||||
LOG(DEBUG) << "Supplying auth token to Keymaster";
|
||||
addStringParam(¶mBuilder, keymaster::TAG_AUTH_TOKEN, auth.token);
|
||||
}
|
||||
return paramBuilder;
|
||||
}
|
||||
|
||||
/*static bool encryptWithKeymasterKey(Keymaster& keymaster, const std::string& key,
|
||||
const KeyAuthentication& auth, const std::string& appId,
|
||||
const std::string& message, std::string* ciphertext) {
|
||||
auto params = beginParams(auth, appId).build();
|
||||
keymaster::AuthorizationSet outParams;
|
||||
auto opHandle = keymaster.begin(KM_PURPOSE_ENCRYPT, key, params, &outParams);
|
||||
if (!opHandle) return false;
|
||||
keymaster_blob_t nonceBlob;
|
||||
if (!outParams.GetTagValue(keymaster::TAG_NONCE, &nonceBlob)) {
|
||||
LOG(ERROR) << "GCM encryption but no nonce generated";
|
||||
return false;
|
||||
}
|
||||
// nonceBlob here is just a pointer into existing data, must not be freed
|
||||
std::string nonce(reinterpret_cast<const char*>(nonceBlob.data), nonceBlob.data_length);
|
||||
if (!checkSize("nonce", nonce.size(), GCM_NONCE_BYTES)) return false;
|
||||
std::string body;
|
||||
if (!opHandle.updateCompletely(message, &body)) return false;
|
||||
|
||||
std::string mac;
|
||||
if (!opHandle.finishWithOutput(&mac)) return false;
|
||||
if (!checkSize("mac", mac.size(), GCM_MAC_BYTES)) return false;
|
||||
*ciphertext = nonce + body + mac;
|
||||
return true;
|
||||
}*/
|
||||
|
||||
static bool decryptWithKeymasterKey(Keymaster& keymaster, const std::string& key,
|
||||
const KeyAuthentication& auth, const std::string& appId,
|
||||
const std::string& ciphertext, std::string* message) {
|
||||
auto nonce = ciphertext.substr(0, GCM_NONCE_BYTES);
|
||||
auto bodyAndMac = ciphertext.substr(GCM_NONCE_BYTES);
|
||||
auto params = addStringParam(beginParams(auth, appId), keymaster::TAG_NONCE, nonce).build();
|
||||
auto opHandle = keymaster.begin(KM_PURPOSE_DECRYPT, key, params);
|
||||
if (!opHandle) return false;
|
||||
if (!opHandle.updateCompletely(bodyAndMac, message)) return false;
|
||||
if (!opHandle.finish()) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool readFileToString(const std::string& filename, std::string* result) {
|
||||
if (!android::base::ReadFileToString(filename, result)) {
|
||||
PLOG(ERROR) << "Failed to read from " << filename;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*static bool writeStringToFile(const std::string& payload, const std::string& filename) {
|
||||
if (!android::base::WriteStringToFile(payload, filename)) {
|
||||
PLOG(ERROR) << "Failed to write to " << filename;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}*/
|
||||
|
||||
static std::string getStretching() {
|
||||
char paramstr[PROPERTY_VALUE_MAX];
|
||||
|
||||
property_get(SCRYPT_PROP, paramstr, SCRYPT_DEFAULTS);
|
||||
return std::string() + kStretchPrefix_scrypt + paramstr;
|
||||
}
|
||||
|
||||
static bool stretchingNeedsSalt(const std::string& stretching) {
|
||||
return stretching != kStretch_nopassword && stretching != kStretch_none;
|
||||
}
|
||||
|
||||
static bool stretchSecret(const std::string& stretching, const std::string& secret,
|
||||
const std::string& salt, std::string* stretched) {
|
||||
if (stretching == kStretch_nopassword) {
|
||||
if (!secret.empty()) {
|
||||
LOG(WARNING) << "Password present but stretching is nopassword";
|
||||
// Continue anyway
|
||||
}
|
||||
stretched->clear();
|
||||
} else if (stretching == kStretch_none) {
|
||||
*stretched = secret;
|
||||
} else if (std::equal(kStretchPrefix_scrypt.begin(), kStretchPrefix_scrypt.end(),
|
||||
stretching.begin())) {
|
||||
int Nf, rf, pf;
|
||||
if (!parse_scrypt_parameters(stretching.substr(kStretchPrefix_scrypt.size()).c_str(), &Nf,
|
||||
&rf, &pf)) {
|
||||
LOG(ERROR) << "Unable to parse scrypt params in stretching: " << stretching;
|
||||
return false;
|
||||
}
|
||||
stretched->assign(STRETCHED_BYTES, '\0');
|
||||
if (crypto_scrypt(reinterpret_cast<const uint8_t*>(secret.data()), secret.size(),
|
||||
reinterpret_cast<const uint8_t*>(salt.data()), salt.size(),
|
||||
1 << Nf, 1 << rf, 1 << pf,
|
||||
reinterpret_cast<uint8_t*>(&(*stretched)[0]), stretched->size()) != 0) {
|
||||
LOG(ERROR) << "scrypt failed with params: " << stretching;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
LOG(ERROR) << "Unknown stretching type: " << stretching;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool generateAppId(const KeyAuthentication& auth, const std::string& stretching,
|
||||
const std::string& salt, const std::string& secdiscardable,
|
||||
std::string* appId) {
|
||||
std::string stretched;
|
||||
if (!stretchSecret(stretching, auth.secret, salt, &stretched)) return false;
|
||||
*appId = hashSecdiscardable(secdiscardable) + stretched;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*bool storeKey(const std::string& dir, const KeyAuthentication& auth, const std::string& key) {
|
||||
if (TEMP_FAILURE_RETRY(mkdir(dir.c_str(), 0700)) == -1) {
|
||||
PLOG(ERROR) << "key mkdir " << dir;
|
||||
return false;
|
||||
}
|
||||
if (!writeStringToFile(kCurrentVersion, dir + "/" + kFn_version)) return false;
|
||||
std::string secdiscardable;
|
||||
if (ReadRandomBytes(SECDISCARDABLE_BYTES, secdiscardable) != OK) {
|
||||
// TODO status_t plays badly with PLOG, fix it.
|
||||
LOG(ERROR) << "Random read failed";
|
||||
return false;
|
||||
}
|
||||
if (!writeStringToFile(secdiscardable, dir + "/" + kFn_secdiscardable)) return false;
|
||||
std::string stretching = auth.secret.empty() ? kStretch_nopassword : getStretching();
|
||||
if (!writeStringToFile(stretching, dir + "/" + kFn_stretching)) return false;
|
||||
std::string salt;
|
||||
if (stretchingNeedsSalt(stretching)) {
|
||||
if (ReadRandomBytes(SALT_BYTES, salt) != OK) {
|
||||
LOG(ERROR) << "Random read failed";
|
||||
return false;
|
||||
}
|
||||
if (!writeStringToFile(salt, dir + "/" + kFn_salt)) return false;
|
||||
}
|
||||
std::string appId;
|
||||
if (!generateAppId(auth, stretching, salt, secdiscardable, &appId)) return false;
|
||||
Keymaster keymaster;
|
||||
if (!keymaster) return false;
|
||||
std::string kmKey;
|
||||
if (!generateKeymasterKey(keymaster, auth, appId, &kmKey)) return false;
|
||||
if (!writeStringToFile(kmKey, dir + "/" + kFn_keymaster_key_blob)) return false;
|
||||
std::string encryptedKey;
|
||||
if (!encryptWithKeymasterKey(keymaster, kmKey, auth, appId, key, &encryptedKey)) return false;
|
||||
if (!writeStringToFile(encryptedKey, dir + "/" + kFn_encrypted_key)) return false;
|
||||
return true;
|
||||
}*/
|
||||
|
||||
bool retrieveKey(const std::string& dir, const KeyAuthentication& auth, std::string* key) {
|
||||
std::string version;
|
||||
if (!readFileToString(dir + "/" + kFn_version, &version)) return false;
|
||||
if (version != kCurrentVersion) {
|
||||
LOG(ERROR) << "Version mismatch, expected " << kCurrentVersion << " got " << version;
|
||||
return false;
|
||||
}
|
||||
std::string secdiscardable;
|
||||
if (!readFileToString(dir + "/" + kFn_secdiscardable, &secdiscardable)) return false;
|
||||
std::string stretching;
|
||||
if (!readFileToString(dir + "/" + kFn_stretching, &stretching)) return false;
|
||||
std::string salt;
|
||||
if (stretchingNeedsSalt(stretching)) {
|
||||
if (!readFileToString(dir + "/" + kFn_salt, &salt)) return false;
|
||||
}
|
||||
std::string appId;
|
||||
if (!generateAppId(auth, stretching, salt, secdiscardable, &appId)) return false;
|
||||
std::string kmKey;
|
||||
if (!readFileToString(dir + "/" + kFn_keymaster_key_blob, &kmKey)) return false;
|
||||
std::string encryptedMessage;
|
||||
if (!readFileToString(dir + "/" + kFn_encrypted_key, &encryptedMessage)) return false;
|
||||
Keymaster keymaster;
|
||||
if (!keymaster) return false;
|
||||
return decryptWithKeymasterKey(keymaster, kmKey, auth, appId, encryptedMessage, key);
|
||||
}
|
||||
|
||||
static bool deleteKey(const std::string& dir) {
|
||||
std::string kmKey;
|
||||
if (!readFileToString(dir + "/" + kFn_keymaster_key_blob, &kmKey)) return false;
|
||||
Keymaster keymaster;
|
||||
if (!keymaster) return false;
|
||||
if (!keymaster.deleteKey(kmKey)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool secdiscardSecdiscardable(const std::string& dir) {
|
||||
if (ForkExecvp(
|
||||
std::vector<std::string>{kSecdiscardPath, "--", dir + "/" + kFn_secdiscardable}) != 0) {
|
||||
LOG(ERROR) << "secdiscard failed";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool recursiveDeleteKey(const std::string& dir) {
|
||||
if (ForkExecvp(std::vector<std::string>{kRmPath, "-rf", dir}) != 0) {
|
||||
LOG(ERROR) << "recursive delete failed";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool destroyKey(const std::string& dir) {
|
||||
bool success = true;
|
||||
// Try each thing, even if previous things failed.
|
||||
success &= deleteKey(dir);
|
||||
success &= secdiscardSecdiscardable(dir);
|
||||
success &= recursiveDeleteKey(dir);
|
||||
return success;
|
||||
}
|
||||
|
||||
} // namespace vold
|
||||
} // namespace android
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef ANDROID_VOLD_KEYSTORAGE_H
|
||||
#define ANDROID_VOLD_KEYSTORAGE_H
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace android {
|
||||
namespace vold {
|
||||
|
||||
// Represents the information needed to decrypt a disk encryption key.
|
||||
// If "token" is nonempty, it is passed in as a required Gatekeeper auth token.
|
||||
// If "secret" is nonempty, it is appended to the application-specific
|
||||
// binary needed to unlock.
|
||||
class KeyAuthentication {
|
||||
public:
|
||||
KeyAuthentication(std::string t, std::string s) : token{t}, secret{s} {};
|
||||
const std::string token;
|
||||
const std::string secret;
|
||||
};
|
||||
|
||||
extern const KeyAuthentication kEmptyAuthentication;
|
||||
|
||||
// Create a directory at the named path, and store "key" in it,
|
||||
// in such a way that it can only be retrieved via Keymaster and
|
||||
// can be securely deleted.
|
||||
// It's safe to move/rename the directory after creation.
|
||||
//bool storeKey(const std::string& dir, const KeyAuthentication& auth, const std::string& key);
|
||||
|
||||
// Retrieve the key from the named directory.
|
||||
bool retrieveKey(const std::string& dir, const KeyAuthentication& auth, std::string* key);
|
||||
|
||||
// Securely destroy the key stored in the named directory and delete the directory.
|
||||
bool destroyKey(const std::string& dir);
|
||||
|
||||
} // namespace vold
|
||||
} // namespace android
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,254 @@
|
||||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "Keymaster.h"
|
||||
|
||||
//#include <android-base/logging.h>
|
||||
#include <hardware/hardware.h>
|
||||
#include <hardware/keymaster1.h>
|
||||
#include <hardware/keymaster2.h>
|
||||
|
||||
#include <iostream>
|
||||
#define ERROR 1
|
||||
#define LOG(x) std::cout
|
||||
|
||||
namespace android {
|
||||
namespace vold {
|
||||
|
||||
class IKeymasterDevice {
|
||||
public:
|
||||
IKeymasterDevice() {}
|
||||
virtual ~IKeymasterDevice() {}
|
||||
/*virtual keymaster_error_t generate_key(const keymaster_key_param_set_t* params,
|
||||
keymaster_key_blob_t* key_blob) const = 0;*/
|
||||
virtual keymaster_error_t delete_key(const keymaster_key_blob_t* key) const = 0;
|
||||
virtual keymaster_error_t begin(keymaster_purpose_t purpose, const keymaster_key_blob_t* key,
|
||||
const keymaster_key_param_set_t* in_params,
|
||||
keymaster_key_param_set_t* out_params,
|
||||
keymaster_operation_handle_t* operation_handle) const = 0;
|
||||
virtual keymaster_error_t update(keymaster_operation_handle_t operation_handle,
|
||||
const keymaster_key_param_set_t* in_params,
|
||||
const keymaster_blob_t* input, size_t* input_consumed,
|
||||
keymaster_key_param_set_t* out_params,
|
||||
keymaster_blob_t* output) const = 0;
|
||||
virtual keymaster_error_t finish(keymaster_operation_handle_t operation_handle,
|
||||
const keymaster_key_param_set_t* in_params,
|
||||
const keymaster_blob_t* signature,
|
||||
keymaster_key_param_set_t* out_params,
|
||||
keymaster_blob_t* output) const = 0;
|
||||
virtual keymaster_error_t abort(keymaster_operation_handle_t operation_handle) const = 0;
|
||||
|
||||
protected:
|
||||
DISALLOW_COPY_AND_ASSIGN(IKeymasterDevice);
|
||||
};
|
||||
|
||||
template <typename T> class KeymasterDevice : public IKeymasterDevice {
|
||||
public:
|
||||
KeymasterDevice(T* d) : mDevice{d} {}
|
||||
/*keymaster_error_t generate_key(const keymaster_key_param_set_t* params,
|
||||
keymaster_key_blob_t* key_blob) const override final {
|
||||
return mDevice->generate_key(mDevice, params, key_blob, nullptr);
|
||||
}*/
|
||||
keymaster_error_t delete_key(const keymaster_key_blob_t* key) const override final {
|
||||
if (mDevice->delete_key == nullptr) return KM_ERROR_OK;
|
||||
return mDevice->delete_key(mDevice, key);
|
||||
}
|
||||
keymaster_error_t begin(keymaster_purpose_t purpose, const keymaster_key_blob_t* key,
|
||||
const keymaster_key_param_set_t* in_params,
|
||||
keymaster_key_param_set_t* out_params,
|
||||
keymaster_operation_handle_t* operation_handle) const override final {
|
||||
return mDevice->begin(mDevice, purpose, key, in_params, out_params, operation_handle);
|
||||
}
|
||||
keymaster_error_t update(keymaster_operation_handle_t operation_handle,
|
||||
const keymaster_key_param_set_t* in_params,
|
||||
const keymaster_blob_t* input, size_t* input_consumed,
|
||||
keymaster_key_param_set_t* out_params,
|
||||
keymaster_blob_t* output) const override final {
|
||||
return mDevice->update(mDevice, operation_handle, in_params, input, input_consumed,
|
||||
out_params, output);
|
||||
}
|
||||
keymaster_error_t abort(keymaster_operation_handle_t operation_handle) const override final {
|
||||
return mDevice->abort(mDevice, operation_handle);
|
||||
}
|
||||
|
||||
protected:
|
||||
T* const mDevice;
|
||||
};
|
||||
|
||||
class Keymaster1Device : public KeymasterDevice<keymaster1_device_t> {
|
||||
public:
|
||||
Keymaster1Device(keymaster1_device_t* d) : KeymasterDevice<keymaster1_device_t>{d} {}
|
||||
~Keymaster1Device() override final { keymaster1_close(mDevice); }
|
||||
keymaster_error_t finish(keymaster_operation_handle_t operation_handle,
|
||||
const keymaster_key_param_set_t* in_params,
|
||||
const keymaster_blob_t* signature,
|
||||
keymaster_key_param_set_t* out_params,
|
||||
keymaster_blob_t* output) const override final {
|
||||
return mDevice->finish(mDevice, operation_handle, in_params, signature, out_params, output);
|
||||
}
|
||||
};
|
||||
|
||||
class Keymaster2Device : public KeymasterDevice<keymaster2_device_t> {
|
||||
public:
|
||||
Keymaster2Device(keymaster2_device_t* d) : KeymasterDevice<keymaster2_device_t>{d} {}
|
||||
~Keymaster2Device() override final { keymaster2_close(mDevice); }
|
||||
keymaster_error_t finish(keymaster_operation_handle_t operation_handle,
|
||||
const keymaster_key_param_set_t* in_params,
|
||||
const keymaster_blob_t* signature,
|
||||
keymaster_key_param_set_t* out_params,
|
||||
keymaster_blob_t* output) const override final {
|
||||
return mDevice->finish(mDevice, operation_handle, in_params, nullptr, signature, out_params,
|
||||
output);
|
||||
}
|
||||
};
|
||||
|
||||
KeymasterOperation::~KeymasterOperation() {
|
||||
if (mDevice) mDevice->abort(mOpHandle);
|
||||
}
|
||||
|
||||
bool KeymasterOperation::updateCompletely(const std::string& input, std::string* output) {
|
||||
output->clear();
|
||||
auto it = input.begin();
|
||||
while (it != input.end()) {
|
||||
size_t toRead = static_cast<size_t>(input.end() - it);
|
||||
keymaster_blob_t inputBlob{reinterpret_cast<const uint8_t*>(&*it), toRead};
|
||||
keymaster_blob_t outputBlob;
|
||||
size_t inputConsumed;
|
||||
auto error =
|
||||
mDevice->update(mOpHandle, nullptr, &inputBlob, &inputConsumed, nullptr, &outputBlob);
|
||||
if (error != KM_ERROR_OK) {
|
||||
LOG(ERROR) << "update failed, code " << error;
|
||||
mDevice = nullptr;
|
||||
return false;
|
||||
}
|
||||
output->append(reinterpret_cast<const char*>(outputBlob.data), outputBlob.data_length);
|
||||
free(const_cast<uint8_t*>(outputBlob.data));
|
||||
if (inputConsumed > toRead) {
|
||||
LOG(ERROR) << "update reported too much input consumed";
|
||||
mDevice = nullptr;
|
||||
return false;
|
||||
}
|
||||
it += inputConsumed;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool KeymasterOperation::finish() {
|
||||
auto error = mDevice->finish(mOpHandle, nullptr, nullptr, nullptr, nullptr);
|
||||
mDevice = nullptr;
|
||||
if (error != KM_ERROR_OK) {
|
||||
LOG(ERROR) << "finish failed, code " << error;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool KeymasterOperation::finishWithOutput(std::string* output) {
|
||||
keymaster_blob_t outputBlob;
|
||||
auto error = mDevice->finish(mOpHandle, nullptr, nullptr, nullptr, &outputBlob);
|
||||
mDevice = nullptr;
|
||||
if (error != KM_ERROR_OK) {
|
||||
LOG(ERROR) << "finish failed, code " << error;
|
||||
return false;
|
||||
}
|
||||
output->assign(reinterpret_cast<const char*>(outputBlob.data), outputBlob.data_length);
|
||||
free(const_cast<uint8_t*>(outputBlob.data));
|
||||
return true;
|
||||
}
|
||||
|
||||
Keymaster::Keymaster() {
|
||||
mDevice = nullptr;
|
||||
const hw_module_t* module;
|
||||
int ret = hw_get_module_by_class(KEYSTORE_HARDWARE_MODULE_ID, NULL, &module);
|
||||
if (ret != 0) {
|
||||
LOG(ERROR) << "hw_get_module_by_class returned " << ret;
|
||||
return;
|
||||
}
|
||||
if (module->module_api_version == KEYMASTER_MODULE_API_VERSION_1_0) {
|
||||
keymaster1_device_t* device;
|
||||
ret = keymaster1_open(module, &device);
|
||||
if (ret != 0) {
|
||||
LOG(ERROR) << "keymaster1_open returned " << ret;
|
||||
return;
|
||||
}
|
||||
mDevice = std::make_shared<Keymaster1Device>(device);
|
||||
} else if (module->module_api_version == KEYMASTER_MODULE_API_VERSION_2_0) {
|
||||
keymaster2_device_t* device;
|
||||
ret = keymaster2_open(module, &device);
|
||||
if (ret != 0) {
|
||||
LOG(ERROR) << "keymaster2_open returned " << ret;
|
||||
return;
|
||||
}
|
||||
mDevice = std::make_shared<Keymaster2Device>(device);
|
||||
} else {
|
||||
LOG(ERROR) << "module_api_version is " << module->module_api_version;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*bool Keymaster::generateKey(const keymaster::AuthorizationSet& inParams, std::string* key) {
|
||||
keymaster_key_blob_t keyBlob;
|
||||
auto error = mDevice->generate_key(&inParams, &keyBlob);
|
||||
if (error != KM_ERROR_OK) {
|
||||
LOG(ERROR) << "generate_key failed, code " << error;
|
||||
return false;
|
||||
}
|
||||
key->assign(reinterpret_cast<const char*>(keyBlob.key_material), keyBlob.key_material_size);
|
||||
free(const_cast<uint8_t*>(keyBlob.key_material));
|
||||
return true;
|
||||
}*/
|
||||
|
||||
bool Keymaster::deleteKey(const std::string& key) {
|
||||
keymaster_key_blob_t keyBlob{reinterpret_cast<const uint8_t*>(key.data()), key.size()};
|
||||
auto error = mDevice->delete_key(&keyBlob);
|
||||
if (error != KM_ERROR_OK) {
|
||||
LOG(ERROR) << "delete_key failed, code " << error;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
KeymasterOperation Keymaster::begin(keymaster_purpose_t purpose, const std::string& key,
|
||||
const keymaster::AuthorizationSet& inParams,
|
||||
keymaster::AuthorizationSet* outParams) {
|
||||
keymaster_key_blob_t keyBlob{reinterpret_cast<const uint8_t*>(key.data()), key.size()};
|
||||
keymaster_operation_handle_t mOpHandle;
|
||||
keymaster_key_param_set_t outParams_set;
|
||||
auto error = mDevice->begin(purpose, &keyBlob, &inParams, &outParams_set, &mOpHandle);
|
||||
if (error != KM_ERROR_OK) {
|
||||
LOG(ERROR) << "begin failed, code " << error;
|
||||
return KeymasterOperation(nullptr, mOpHandle);
|
||||
}
|
||||
outParams->Clear();
|
||||
outParams->push_back(outParams_set);
|
||||
keymaster_free_param_set(&outParams_set);
|
||||
return KeymasterOperation(mDevice, mOpHandle);
|
||||
}
|
||||
|
||||
KeymasterOperation Keymaster::begin(keymaster_purpose_t purpose, const std::string& key,
|
||||
const keymaster::AuthorizationSet& inParams) {
|
||||
keymaster_key_blob_t keyBlob{reinterpret_cast<const uint8_t*>(key.data()), key.size()};
|
||||
keymaster_operation_handle_t mOpHandle;
|
||||
auto error = mDevice->begin(purpose, &keyBlob, &inParams, nullptr, &mOpHandle);
|
||||
if (error != KM_ERROR_OK) {
|
||||
LOG(ERROR) << "begin failed, code " << error;
|
||||
return KeymasterOperation(nullptr, mOpHandle);
|
||||
}
|
||||
return KeymasterOperation(mDevice, mOpHandle);
|
||||
}
|
||||
|
||||
} // namespace vold
|
||||
} // namespace android
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* 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>
|
||||
|
||||
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
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "ScryptParameters.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
bool parse_scrypt_parameters(const char* paramstr, int *Nf, int *rf, int *pf) {
|
||||
int params[3];
|
||||
char *token;
|
||||
char *saveptr;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* The token we're looking for should be three integers separated by
|
||||
* colons (e.g., "12:8:1"). Scan the property to make sure it matches.
|
||||
*/
|
||||
for (i = 0, token = strtok_r(const_cast<char *>(paramstr), ":", &saveptr);
|
||||
token != nullptr && i < 3;
|
||||
i++, token = strtok_r(nullptr, ":", &saveptr)) {
|
||||
char *endptr;
|
||||
params[i] = strtol(token, &endptr, 10);
|
||||
|
||||
/*
|
||||
* Check that there was a valid number and it's 8-bit.
|
||||
*/
|
||||
if ((*token == '\0') || (*endptr != '\0') || params[i] < 0 || params[i] > 255) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (token != nullptr) {
|
||||
return false;
|
||||
}
|
||||
*Nf = params[0]; *rf = params[1]; *pf = params[2];
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef ANDROID_VOLD_SCRYPT_PARAMETERS_H
|
||||
#define ANDROID_VOLD_SCRYPT_PARAMETERS_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
#define SCRYPT_PROP "ro.crypto.scrypt_params"
|
||||
#define SCRYPT_DEFAULTS "15:3:1"
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
bool parse_scrypt_parameters(const char* paramstr, int *Nf, int *rf, int *pf);
|
||||
|
||||
__END_DECLS
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,297 @@
|
||||
/*
|
||||
* 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";
|
||||
abort();
|
||||
}
|
||||
abort();
|
||||
status_t res = 1;//android_fork_execvp(argc, argv, NULL, false, true);
|
||||
if (setexeccon(nullptr)) {
|
||||
LOG(ERROR) << "Failed to setexeccon";
|
||||
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";
|
||||
abort();
|
||||
}
|
||||
FILE* fp = popen(cmd.c_str(), "r");
|
||||
if (setexeccon(nullptr)) {
|
||||
LOG(ERROR) << "Failed to setexeccon";
|
||||
abort();
|
||||
}
|
||||
|
||||
if (!fp) {
|
||||
PLOG(ERROR) << "Failed to popen " << cmd;
|
||||
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;
|
||||
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";
|
||||
}
|
||||
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
if (pid == -1) {
|
||||
PLOG(ERROR) << "Failed to exec";
|
||||
}
|
||||
|
||||
free(argv);
|
||||
return pid;
|
||||
}
|
||||
|
||||
status_t ReadRandomBytes(size_t bytes, std::string& out) {
|
||||
out.clear();
|
||||
|
||||
int fd = TEMP_FAILURE_RETRY(open("/dev/urandom", O_RDONLY | O_CLOEXEC | O_NOFOLLOW));
|
||||
if (fd == -1) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
char buf[BUFSIZ];
|
||||
size_t n;
|
||||
while ((n = TEMP_FAILURE_RETRY(read(fd, &buf[0], std::min(sizeof(buf), bytes)))) > 0) {
|
||||
out.append(buf, n);
|
||||
bytes -= 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);
|
||||
}
|
||||
|
||||
// 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
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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>
|
||||
|
||||
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);
|
||||
|
||||
/* 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 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
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Team Win Recovery Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "Decrypt.h"
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
bool ret = false;
|
||||
if (argc < 2) {
|
||||
Decrypt_DE();
|
||||
ret = Decrypt_User(0, "0000");
|
||||
} else if (argc < 3) {
|
||||
Decrypt_DE();
|
||||
ret = Decrypt_User(0, argv[1]);
|
||||
} else {
|
||||
ret = Decrypt_User(atoi(argv[1]), argv[2]);
|
||||
}
|
||||
if (!ret)
|
||||
printf("Failed to decrypt\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -540,6 +540,7 @@
|
||||
<string name="fail_decrypt">Failed to decrypt data.</string>
|
||||
<string name="no_crypto_support">No crypto support was compiled into this build.</string>
|
||||
<string name="decrypt_success_dev">Data successfully decrypted, new block device: '{1}'</string>
|
||||
<string name="decrypt_success_nodev">Data successfully decrypted</string>
|
||||
<string name="done">Done.</string>
|
||||
<string name="start_partition_sd">Partitioning SD Card...</string>
|
||||
<string name="partition_sd_locate">Unable to locate device to partition.</string>
|
||||
|
||||
+59
-3
@@ -58,6 +58,10 @@ extern "C" {
|
||||
#ifdef TW_INCLUDE_CRYPTO
|
||||
#include "crypto/lollipop/cryptfs.h"
|
||||
#include "gpt/gpt.h"
|
||||
#ifdef TW_INCLUDE_FBE
|
||||
#include "crypto/ext4crypt/Decrypt.h"
|
||||
//#include "crypto/ext4crypt/Ext4Crypt.h"
|
||||
#endif
|
||||
#else
|
||||
#define CRYPT_FOOTER_OFFSET 0x4000
|
||||
#endif
|
||||
@@ -204,6 +208,7 @@ TWPartition::TWPartition() {
|
||||
Can_Be_Encrypted = false;
|
||||
Is_Encrypted = false;
|
||||
Is_Decrypted = false;
|
||||
Is_FBE = false;
|
||||
Mount_To_Decrypt = false;
|
||||
Decrypted_Block_Device = "";
|
||||
Display_Name = "";
|
||||
@@ -474,6 +479,11 @@ void TWPartition::Partition_Post_Processing(bool Display_Error) {
|
||||
Setup_Cache_Partition(Display_Error);
|
||||
}
|
||||
|
||||
void TWPartition::ExcludeAll(const string& path) {
|
||||
backup_exclusions.add_absolute_dir(path);
|
||||
wipe_exclusions.add_absolute_dir(path);
|
||||
}
|
||||
|
||||
void TWPartition::Setup_Data_Partition(bool Display_Error) {
|
||||
if (Mount_Point != "/data")
|
||||
return;
|
||||
@@ -491,6 +501,8 @@ void TWPartition::Setup_Data_Partition(bool Display_Error) {
|
||||
DataManager::SetValue(TW_IS_DECRYPTED, 1);
|
||||
Is_Encrypted = true;
|
||||
Is_Decrypted = true;
|
||||
Is_FBE = false;
|
||||
DataManager::SetValue(TW_IS_FBE, 0);
|
||||
Decrypted_Block_Device = crypto_blkdev;
|
||||
LOGINFO("Data already decrypted, new block device: '%s'\n", crypto_blkdev);
|
||||
} else if (!Mount(false)) {
|
||||
@@ -513,9 +525,49 @@ void TWPartition::Setup_Data_Partition(bool Display_Error) {
|
||||
LOGERR("Primary block device '%s' for mount point '%s' is not present!\n", Primary_Block_Device.c_str(), Mount_Point.c_str());
|
||||
}
|
||||
} else {
|
||||
// Filesystem is not encrypted and the mount succeeded, so return to
|
||||
// the original unmounted state
|
||||
UnMount(false);
|
||||
if (TWFunc::Path_Exists("/data/unencrypted/key/version")) {
|
||||
LOGINFO("File Based Encryption is present\n");
|
||||
#ifdef TW_INCLUDE_FBE
|
||||
ExcludeAll(Mount_Point + "/convert_fbe");
|
||||
ExcludeAll(Mount_Point + "/unencrypted");
|
||||
//ExcludeAll(Mount_Point + "/system/users/0"); // we WILL need to retain some of this if multiple users are present or we just need to delete more folders for the extra users somewhere else
|
||||
ExcludeAll(Mount_Point + "/misc/vold/user_keys");
|
||||
ExcludeAll(Mount_Point + "/system_ce");
|
||||
ExcludeAll(Mount_Point + "/system_de");
|
||||
ExcludeAll(Mount_Point + "/misc_ce");
|
||||
ExcludeAll(Mount_Point + "/misc_de");
|
||||
ExcludeAll(Mount_Point + "/system/gatekeeper.password.key");
|
||||
ExcludeAll(Mount_Point + "/system/gatekeeper.pattern.key");
|
||||
ExcludeAll(Mount_Point + "/system/locksettings.db");
|
||||
//ExcludeAll(Mount_Point + "/system/locksettings.db-shm"); // don't seem to need this one, but the other 2 are needed
|
||||
ExcludeAll(Mount_Point + "/system/locksettings.db-wal");
|
||||
ExcludeAll(Mount_Point + "/user_de");
|
||||
//ExcludeAll(Mount_Point + "/misc/profiles/cur/0"); // might be important later
|
||||
ExcludeAll(Mount_Point + "/misc/gatekeeper");
|
||||
ExcludeAll(Mount_Point + "/drm/kek.dat");
|
||||
int retry_count = 3;
|
||||
while (!Decrypt_DE() && --retry_count)
|
||||
usleep(2000);
|
||||
if (retry_count > 0) {
|
||||
property_set("ro.crypto.state", "encrypted");
|
||||
Is_Encrypted = true;
|
||||
Is_Decrypted = false;
|
||||
Is_FBE = true;
|
||||
DataManager::SetValue(TW_IS_FBE, 1);
|
||||
DataManager::SetValue(TW_IS_ENCRYPTED, 1);
|
||||
string filename;
|
||||
DataManager::SetValue(TW_CRYPTO_PWTYPE, Get_Password_Type(0, filename));
|
||||
DataManager::SetValue(TW_CRYPTO_PASSWORD, "");
|
||||
DataManager::SetValue("tw_crypto_display", "");
|
||||
}
|
||||
#else
|
||||
LOGERR("FBE found but FBE support not present in TWRP\n");
|
||||
#endif
|
||||
} else {
|
||||
// Filesystem is not encrypted and the mount succeeded, so return to
|
||||
// the original unmounted state
|
||||
UnMount(false);
|
||||
}
|
||||
}
|
||||
if (datamedia && (!Is_Encrypted || (Is_Encrypted && Is_Decrypted))) {
|
||||
Setup_Data_Media();
|
||||
@@ -2555,6 +2607,10 @@ void TWPartition::Recreate_Media_Folder(void) {
|
||||
string Command;
|
||||
string Media_Path = Mount_Point + "/media";
|
||||
|
||||
if (Is_FBE) {
|
||||
LOGINFO("Not recreating media folder on FBE\n");
|
||||
return;
|
||||
}
|
||||
if (!Mount(true)) {
|
||||
gui_msg(Msg(msg::kError, "recreate_folder_err=Unable to recreate {1} folder.")(Media_Path));
|
||||
} else if (!TWFunc::Path_Exists(Media_Path)) {
|
||||
|
||||
+73
-32
@@ -62,6 +62,9 @@ extern "C" {
|
||||
#include "crypto/lollipop/cryptfs.h"
|
||||
#include "gui/rapidxml.hpp"
|
||||
#include "gui/pages.hpp"
|
||||
#ifdef TW_INCLUDE_FBE
|
||||
#include "crypto/ext4crypt/Decrypt.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef AB_OTA_UPDATER
|
||||
@@ -177,17 +180,28 @@ int TWPartitionManager::Process_Fstab(string Fstab_Filename, bool Display_Error)
|
||||
#ifdef TW_INCLUDE_CRYPTO
|
||||
TWPartition* Decrypt_Data = Find_Partition_By_Path("/data");
|
||||
if (Decrypt_Data && Decrypt_Data->Is_Encrypted && !Decrypt_Data->Is_Decrypted) {
|
||||
int password_type = cryptfs_get_password_type();
|
||||
if (password_type == CRYPT_TYPE_DEFAULT) {
|
||||
LOGINFO("Device is encrypted with the default password, attempting to decrypt.\n");
|
||||
if (Decrypt_Device("default_password") == 0) {
|
||||
gui_msg("decrypt_success=Successfully decrypted with default password.");
|
||||
DataManager::SetValue(TW_IS_ENCRYPTED, 0);
|
||||
} else {
|
||||
gui_err("unable_to_decrypt=Unable to decrypt with default password.");
|
||||
if (Decrypt_Data->Is_FBE) {
|
||||
if (DataManager::GetIntValue(TW_CRYPTO_PWTYPE) == 0) {
|
||||
if (Decrypt_Device("!") == 0) {
|
||||
gui_msg("decrypt_success=Successfully decrypted with default password.");
|
||||
DataManager::SetValue(TW_IS_ENCRYPTED, 0);
|
||||
} else {
|
||||
gui_err("unable_to_decrypt=Unable to decrypt with default password.");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
DataManager::SetValue("TW_CRYPTO_TYPE", password_type);
|
||||
int password_type = cryptfs_get_password_type();
|
||||
if (password_type == CRYPT_TYPE_DEFAULT) {
|
||||
LOGINFO("Device is encrypted with the default password, attempting to decrypt.\n");
|
||||
if (Decrypt_Device("default_password") == 0) {
|
||||
gui_msg("decrypt_success=Successfully decrypted with default password.");
|
||||
DataManager::SetValue(TW_IS_ENCRYPTED, 0);
|
||||
} else {
|
||||
gui_err("unable_to_decrypt=Unable to decrypt with default password.");
|
||||
}
|
||||
} else {
|
||||
DataManager::SetValue("TW_CRYPTO_TYPE", password_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Decrypt_Data && (!Decrypt_Data->Is_Encrypted || Decrypt_Data->Is_Decrypted) && Decrypt_Data->Mount(false)) {
|
||||
@@ -1473,6 +1487,36 @@ void TWPartitionManager::Update_System_Details(void) {
|
||||
return;
|
||||
}
|
||||
|
||||
void TWPartitionManager::Post_Decrypt(const string& Block_Device) {
|
||||
TWPartition* dat = Find_Partition_By_Path("/data");
|
||||
if (dat != NULL) {
|
||||
DataManager::SetValue(TW_IS_DECRYPTED, 1);
|
||||
dat->Is_Decrypted = true;
|
||||
if (!Block_Device.empty()) {
|
||||
dat->Decrypted_Block_Device = Block_Device;
|
||||
gui_msg(Msg("decrypt_success_dev=Data successfully decrypted, new block device: '{1}'")(Block_Device));
|
||||
} else {
|
||||
gui_msg("decrypt_success_nodev=Data successfully decrypted");
|
||||
}
|
||||
dat->Setup_File_System(false);
|
||||
dat->Current_File_System = dat->Fstab_File_System; // Needed if we're ignoring blkid because encrypted devices start out as emmc
|
||||
|
||||
// Sleep for a bit so that the device will be ready
|
||||
sleep(1);
|
||||
if (dat->Has_Data_Media && dat->Mount(false) && TWFunc::Path_Exists("/data/media/0")) {
|
||||
dat->Storage_Path = "/data/media/0";
|
||||
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);
|
||||
Output_Partition(dat);
|
||||
}
|
||||
Update_System_Details();
|
||||
UnMount_Main_Partitions();
|
||||
} else
|
||||
LOGERR("Unable to locate data partition.\n");
|
||||
}
|
||||
|
||||
int TWPartitionManager::Decrypt_Device(string Password) {
|
||||
#ifdef TW_INCLUDE_CRYPTO
|
||||
int ret_val, password_len;
|
||||
@@ -1494,6 +1538,25 @@ int TWPartitionManager::Decrypt_Device(string Password) {
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
if (DataManager::GetIntValue(TW_IS_FBE)) {
|
||||
#ifdef TW_INCLUDE_FBE
|
||||
if (!Mount_By_Path("/data", true)) // /data has to be mounted for FBE
|
||||
return -1;
|
||||
int retry_count = 10;
|
||||
while (!TWFunc::Path_Exists("/data/system/users/gatekeeper.password.key") && --retry_count)
|
||||
usleep(2000); // A small sleep is needed after mounting /data to ensure reliable decrypt... maybe because of DE?
|
||||
int user_id = DataManager::GetIntValue("tw_decrypt_user_id");
|
||||
LOGINFO("Decrypting FBE for user %i\n", user_id);
|
||||
if (Decrypt_User(user_id, Password)) {
|
||||
Post_Decrypt("");
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
LOGERR("FBE support is not present\n");
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
strcpy(cPassword, Password.c_str());
|
||||
int pwret = cryptfs_check_passwd(cPassword);
|
||||
|
||||
@@ -1513,29 +1576,7 @@ int TWPartitionManager::Decrypt_Device(string Password) {
|
||||
if (strcmp(crypto_blkdev, "error") == 0) {
|
||||
LOGERR("Error retrieving decrypted data block device.\n");
|
||||
} else {
|
||||
TWPartition* dat = Find_Partition_By_Path("/data");
|
||||
if (dat != NULL) {
|
||||
DataManager::SetValue(TW_IS_DECRYPTED, 1);
|
||||
dat->Is_Decrypted = true;
|
||||
dat->Decrypted_Block_Device = crypto_blkdev;
|
||||
dat->Setup_File_System(false);
|
||||
dat->Current_File_System = dat->Fstab_File_System; // Needed if we're ignoring blkid because encrypted devices start out as emmc
|
||||
gui_msg(Msg("decrypt_success_dev=Data successfully decrypted, new block device: '{1}'")(crypto_blkdev));
|
||||
|
||||
// Sleep for a bit so that the device will be ready
|
||||
sleep(1);
|
||||
if (dat->Has_Data_Media && dat->Mount(false) && TWFunc::Path_Exists("/data/media/0")) {
|
||||
dat->Storage_Path = "/data/media/0";
|
||||
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);
|
||||
Output_Partition(dat);
|
||||
}
|
||||
Update_System_Details();
|
||||
UnMount_Main_Partitions();
|
||||
} else
|
||||
LOGERR("Unable to locate data partition.\n");
|
||||
Post_Decrypt(crypto_blkdev);
|
||||
}
|
||||
return 0;
|
||||
#else
|
||||
|
||||
@@ -166,6 +166,7 @@ private:
|
||||
bool Is_Sparse_Image(const string& Filename); // Determines if a file is in sparse image format
|
||||
bool Flash_Sparse_Image(const string& Filename); // Flashes a sparse image using simg2img
|
||||
bool Flash_Image_FI(const string& Filename, ProgressTracking *progress); // Flashes an image to the partition using flash_image for mtd nand
|
||||
void ExcludeAll(const string& path); // Adds an exclusion for path to both the backup and wipe exclusion lists
|
||||
|
||||
private:
|
||||
bool Can_Be_Mounted; // Indicates that the partition can be mounted
|
||||
@@ -194,6 +195,7 @@ private:
|
||||
bool Can_Be_Encrypted; // This partition might be encrypted, affects error handling, can only be true if crypto support is compiled in
|
||||
bool Is_Encrypted; // This partition is thought to be encrypted -- it wouldn't mount for some reason, only avialble with crypto support
|
||||
bool Is_Decrypted; // This partition has successfully been decrypted
|
||||
bool Is_FBE; // File Based Encryption is present
|
||||
bool Mount_To_Decrypt; // Mount this partition during decrypt (/vendor, /firmware, etc in case we need proprietary libs or firmware files)
|
||||
string Display_Name; // Display name for the GUI
|
||||
string Backup_Name; // Backup name -- used for backup filenames
|
||||
@@ -304,6 +306,7 @@ private:
|
||||
bool Add_Remove_MTP_Storage(TWPartition* Part, int message_type); // Adds or removes an MTP Storage partition
|
||||
TWPartition* Find_Next_Storage(string Path, bool Exclude_Data_Media);
|
||||
int Open_Lun_File(string Partition_Path, string Lun_File);
|
||||
void Post_Decrypt(const string& Block_Device); // Completes various post-decrypt tasks
|
||||
pid_t mtppid;
|
||||
bool mtp_was_enabled;
|
||||
int mtp_write_fd;
|
||||
|
||||
@@ -176,6 +176,18 @@ ifeq ($(TW_INCLUDE_CRYPTO), true)
|
||||
ifeq ($(TARGET_HW_DISK_ENCRYPTION),true)
|
||||
RELINK_SOURCE_FILES += $(TARGET_OUT_VENDOR_SHARED_LIBRARIES)/libcryptfs_hw.so
|
||||
endif
|
||||
# FBE files
|
||||
ifeq ($(TW_INCLUDE_CRYPTO_FBE), true)
|
||||
RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libe4crypt.so
|
||||
RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libgatekeeper.so
|
||||
RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libsoftkeymaster.so
|
||||
RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libkeymaster_messages.so
|
||||
RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libkeystore_binder.so
|
||||
RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libbinder.so
|
||||
RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libprotobuf-cpp-lite.so
|
||||
RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libsoftkeymasterdevice.so
|
||||
RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libkeymaster1.so
|
||||
endif
|
||||
endif
|
||||
ifeq ($(AB_OTA_UPDATER), true)
|
||||
RELINK_SOURCE_FILES += $(TARGET_OUT_EXECUTABLES)/bootctl
|
||||
|
||||
@@ -132,6 +132,7 @@
|
||||
#define TW_IS_DECRYPTED "tw_is_decrypted"
|
||||
#define TW_CRYPTO_PWTYPE "tw_crypto_pwtype"
|
||||
#define TW_HAS_CRYPTO "tw_has_crypto"
|
||||
#define TW_IS_FBE "tw_is_fbe"
|
||||
#define TW_CRYPTO_PASSWORD "tw_crypto_password"
|
||||
#define TW_SDEXT_DISABLE_EXT4 "tw_sdext_disable_ext4"
|
||||
#define TW_MILITARY_TIME "tw_military_time"
|
||||
|
||||
Reference in New Issue
Block a user