Support File Based Encryption

Change-Id: Ib688ddd0c32d3999590cacd86b6d9b18eac336e9
This commit is contained in:
Ethan Yonker
2016-12-07 13:55:01 -06:00
committed by Dees Troy
parent 1b190166eb
commit bd7492de28
23 changed files with 2271 additions and 35 deletions
+8
View File
@@ -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)
+33
View File
@@ -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
+160
View File
@@ -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;
}
+34
View File
@@ -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
+521
View File
@@ -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;
}
+43
View File
@@ -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
+53
View File
@@ -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;
}
+24
View File
@@ -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
+349
View File
@@ -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(&paramBuilder, keymaster::TAG_APPLICATION_ID, appId);
if (auth.token.empty()) {
LOG(DEBUG) << "Creating key that doesn't need auth token";
paramBuilder.Authorization(keymaster::TAG_NO_AUTH_REQUIRED);
} else {
LOG(DEBUG) << "Auth token required for key";
if (auth.token.size() != sizeof(hw_auth_token_t)) {
LOG(ERROR) << "Auth token should be " << sizeof(hw_auth_token_t) << " bytes, was "
<< auth.token.size() << " bytes";
return false;
}
const hw_auth_token_t* at = reinterpret_cast<const hw_auth_token_t*>(auth.token.data());
paramBuilder.Authorization(keymaster::TAG_USER_SECURE_ID, at->user_id);
paramBuilder.Authorization(keymaster::TAG_USER_AUTH_TYPE, HW_AUTH_PASSWORD);
paramBuilder.Authorization(keymaster::TAG_AUTH_TIMEOUT, AUTH_TIMEOUT);
}
return keymaster.generateKey(paramBuilder.build(), key);
}*/
static keymaster::AuthorizationSetBuilder beginParams(const KeyAuthentication& auth,
const std::string& appId) {
auto paramBuilder = keymaster::AuthorizationSetBuilder()
.Authorization(keymaster::TAG_BLOCK_MODE, KM_MODE_GCM)
.Authorization(keymaster::TAG_MAC_LENGTH, GCM_MAC_BYTES * 8)
.Authorization(keymaster::TAG_PADDING, KM_PAD_NONE);
addStringParam(&paramBuilder, keymaster::TAG_APPLICATION_ID, appId);
if (!auth.token.empty()) {
LOG(DEBUG) << "Supplying auth token to Keymaster";
addStringParam(&paramBuilder, keymaster::TAG_AUTH_TOKEN, auth.token);
}
return paramBuilder;
}
/*static bool encryptWithKeymasterKey(Keymaster& keymaster, const std::string& key,
const KeyAuthentication& auth, const std::string& appId,
const std::string& message, std::string* ciphertext) {
auto params = beginParams(auth, appId).build();
keymaster::AuthorizationSet outParams;
auto opHandle = keymaster.begin(KM_PURPOSE_ENCRYPT, key, params, &outParams);
if (!opHandle) return false;
keymaster_blob_t nonceBlob;
if (!outParams.GetTagValue(keymaster::TAG_NONCE, &nonceBlob)) {
LOG(ERROR) << "GCM encryption but no nonce generated";
return false;
}
// nonceBlob here is just a pointer into existing data, must not be freed
std::string nonce(reinterpret_cast<const char*>(nonceBlob.data), nonceBlob.data_length);
if (!checkSize("nonce", nonce.size(), GCM_NONCE_BYTES)) return false;
std::string body;
if (!opHandle.updateCompletely(message, &body)) return false;
std::string mac;
if (!opHandle.finishWithOutput(&mac)) return false;
if (!checkSize("mac", mac.size(), GCM_MAC_BYTES)) return false;
*ciphertext = nonce + body + mac;
return true;
}*/
static bool decryptWithKeymasterKey(Keymaster& keymaster, const std::string& key,
const KeyAuthentication& auth, const std::string& appId,
const std::string& ciphertext, std::string* message) {
auto nonce = ciphertext.substr(0, GCM_NONCE_BYTES);
auto bodyAndMac = ciphertext.substr(GCM_NONCE_BYTES);
auto params = addStringParam(beginParams(auth, appId), keymaster::TAG_NONCE, nonce).build();
auto opHandle = keymaster.begin(KM_PURPOSE_DECRYPT, key, params);
if (!opHandle) return false;
if (!opHandle.updateCompletely(bodyAndMac, message)) return false;
if (!opHandle.finish()) return false;
return true;
}
static bool readFileToString(const std::string& filename, std::string* result) {
if (!android::base::ReadFileToString(filename, result)) {
PLOG(ERROR) << "Failed to read from " << filename;
return false;
}
return true;
}
/*static bool writeStringToFile(const std::string& payload, const std::string& filename) {
if (!android::base::WriteStringToFile(payload, filename)) {
PLOG(ERROR) << "Failed to write to " << filename;
return false;
}
return true;
}*/
static std::string getStretching() {
char paramstr[PROPERTY_VALUE_MAX];
property_get(SCRYPT_PROP, paramstr, SCRYPT_DEFAULTS);
return std::string() + kStretchPrefix_scrypt + paramstr;
}
static bool stretchingNeedsSalt(const std::string& stretching) {
return stretching != kStretch_nopassword && stretching != kStretch_none;
}
static bool stretchSecret(const std::string& stretching, const std::string& secret,
const std::string& salt, std::string* stretched) {
if (stretching == kStretch_nopassword) {
if (!secret.empty()) {
LOG(WARNING) << "Password present but stretching is nopassword";
// Continue anyway
}
stretched->clear();
} else if (stretching == kStretch_none) {
*stretched = secret;
} else if (std::equal(kStretchPrefix_scrypt.begin(), kStretchPrefix_scrypt.end(),
stretching.begin())) {
int Nf, rf, pf;
if (!parse_scrypt_parameters(stretching.substr(kStretchPrefix_scrypt.size()).c_str(), &Nf,
&rf, &pf)) {
LOG(ERROR) << "Unable to parse scrypt params in stretching: " << stretching;
return false;
}
stretched->assign(STRETCHED_BYTES, '\0');
if (crypto_scrypt(reinterpret_cast<const uint8_t*>(secret.data()), secret.size(),
reinterpret_cast<const uint8_t*>(salt.data()), salt.size(),
1 << Nf, 1 << rf, 1 << pf,
reinterpret_cast<uint8_t*>(&(*stretched)[0]), stretched->size()) != 0) {
LOG(ERROR) << "scrypt failed with params: " << stretching;
return false;
}
} else {
LOG(ERROR) << "Unknown stretching type: " << stretching;
return false;
}
return true;
}
static bool generateAppId(const KeyAuthentication& auth, const std::string& stretching,
const std::string& salt, const std::string& secdiscardable,
std::string* appId) {
std::string stretched;
if (!stretchSecret(stretching, auth.secret, salt, &stretched)) return false;
*appId = hashSecdiscardable(secdiscardable) + stretched;
return true;
}
/*bool storeKey(const std::string& dir, const KeyAuthentication& auth, const std::string& key) {
if (TEMP_FAILURE_RETRY(mkdir(dir.c_str(), 0700)) == -1) {
PLOG(ERROR) << "key mkdir " << dir;
return false;
}
if (!writeStringToFile(kCurrentVersion, dir + "/" + kFn_version)) return false;
std::string secdiscardable;
if (ReadRandomBytes(SECDISCARDABLE_BYTES, secdiscardable) != OK) {
// TODO status_t plays badly with PLOG, fix it.
LOG(ERROR) << "Random read failed";
return false;
}
if (!writeStringToFile(secdiscardable, dir + "/" + kFn_secdiscardable)) return false;
std::string stretching = auth.secret.empty() ? kStretch_nopassword : getStretching();
if (!writeStringToFile(stretching, dir + "/" + kFn_stretching)) return false;
std::string salt;
if (stretchingNeedsSalt(stretching)) {
if (ReadRandomBytes(SALT_BYTES, salt) != OK) {
LOG(ERROR) << "Random read failed";
return false;
}
if (!writeStringToFile(salt, dir + "/" + kFn_salt)) return false;
}
std::string appId;
if (!generateAppId(auth, stretching, salt, secdiscardable, &appId)) return false;
Keymaster keymaster;
if (!keymaster) return false;
std::string kmKey;
if (!generateKeymasterKey(keymaster, auth, appId, &kmKey)) return false;
if (!writeStringToFile(kmKey, dir + "/" + kFn_keymaster_key_blob)) return false;
std::string encryptedKey;
if (!encryptWithKeymasterKey(keymaster, kmKey, auth, appId, key, &encryptedKey)) return false;
if (!writeStringToFile(encryptedKey, dir + "/" + kFn_encrypted_key)) return false;
return true;
}*/
bool retrieveKey(const std::string& dir, const KeyAuthentication& auth, std::string* key) {
std::string version;
if (!readFileToString(dir + "/" + kFn_version, &version)) return false;
if (version != kCurrentVersion) {
LOG(ERROR) << "Version mismatch, expected " << kCurrentVersion << " got " << version;
return false;
}
std::string secdiscardable;
if (!readFileToString(dir + "/" + kFn_secdiscardable, &secdiscardable)) return false;
std::string stretching;
if (!readFileToString(dir + "/" + kFn_stretching, &stretching)) return false;
std::string salt;
if (stretchingNeedsSalt(stretching)) {
if (!readFileToString(dir + "/" + kFn_salt, &salt)) return false;
}
std::string appId;
if (!generateAppId(auth, stretching, salt, secdiscardable, &appId)) return false;
std::string kmKey;
if (!readFileToString(dir + "/" + kFn_keymaster_key_blob, &kmKey)) return false;
std::string encryptedMessage;
if (!readFileToString(dir + "/" + kFn_encrypted_key, &encryptedMessage)) return false;
Keymaster keymaster;
if (!keymaster) return false;
return decryptWithKeymasterKey(keymaster, kmKey, auth, appId, encryptedMessage, key);
}
static bool deleteKey(const std::string& dir) {
std::string kmKey;
if (!readFileToString(dir + "/" + kFn_keymaster_key_blob, &kmKey)) return false;
Keymaster keymaster;
if (!keymaster) return false;
if (!keymaster.deleteKey(kmKey)) return false;
return true;
}
static bool secdiscardSecdiscardable(const std::string& dir) {
if (ForkExecvp(
std::vector<std::string>{kSecdiscardPath, "--", dir + "/" + kFn_secdiscardable}) != 0) {
LOG(ERROR) << "secdiscard failed";
return false;
}
return true;
}
static bool recursiveDeleteKey(const std::string& dir) {
if (ForkExecvp(std::vector<std::string>{kRmPath, "-rf", dir}) != 0) {
LOG(ERROR) << "recursive delete failed";
return false;
}
return true;
}
bool destroyKey(const std::string& dir) {
bool success = true;
// Try each thing, even if previous things failed.
success &= deleteKey(dir);
success &= secdiscardSecdiscardable(dir);
success &= recursiveDeleteKey(dir);
return success;
}
} // namespace vold
} // namespace android
+53
View File
@@ -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
+254
View File
@@ -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
+110
View File
@@ -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
+50
View File
@@ -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;
}
+32
View File
@@ -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
+297
View File
@@ -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
+65
View File
@@ -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
+36
View File
@@ -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;
}
+1
View File
@@ -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
View File
@@ -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
View File
@@ -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
+3
View File
@@ -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;
+12
View File
@@ -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
+1
View File
@@ -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"