Files
android_bootable_recovery/crypto/fscrypt/Decrypt.cpp
bigbiff ad58e1bfae repack: move sbin to /system ramdisk and update repacking
for android-10

This will support updating the ramdisk to a different
compression format and co-exist with magisk.

We are also cleaning up and removing non android-10
api makefile actions.

We are also moving twrp repacking to its own class. We
check the new ramdisk format and if it's different
we have magisk compress using the new ramdisk format.

Change-Id: I770030aae7797e75817178b2f0fccd9f39dc23af
2020-09-08 10:08:33 -04:00

1170 lines
49 KiB
C++
Executable File

/*
* Copyright (C) 2016 - 2020 The TeamWin 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 "FsCrypt.h"
#include <map>
#include <string>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#ifndef HAVE_LIBKEYUTILS
#include "key_control.h"
#else
#include <keyutils.h>
#endif
#include "keystore_client.pb.h"
#include "Weaver1.h"
#include "cutils/properties.h"
#include <openssl/sha.h>
#include <openssl/aes.h>
#include <openssl/evp.h>
#include <openssl/rand.h>
#include <dirent.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <sys/types.h>
#include <fstream>
#include <future>
#include <algorithm>
#include <android-base/file.h>
#include <base/threading/platform_thread.h>
#include <android/hardware/confirmationui/1.0/types.h>
#include <android/security/BnConfirmationPromptCallback.h>
#include <android/security/keystore/IKeystoreService.h>
#include <android/hardware/gatekeeper/1.0/IGatekeeper.h>
#include <binder/IServiceManager.h>
#include <binder/IPCThreadState.h>
#include <hardware/hw_auth_token.h>
#include <keystore/keystore.h>
#include <keystore/keystore_client.h>
#include <keystore/keystore_client_impl.h>
#include <keystore/KeystoreResponse.h>
#include <keystore/keystore_hidl_support.h>
#include <keystore/keystore_promises.h>
#include <keystore/keystore_return_types.h>
#include <keystore/keymaster_types.h>
#include <keymasterV4_0/Keymaster.h>
#include <keystore/OperationResult.h>
#include "keystore_client.pb.h"
#include <keymasterV4_0/authorization_set.h>
#include <keymasterV4_0/keymaster_utils.h>
extern "C" {
#include "crypto_scrypt.h"
}
#include "fscrypt_policy.h"
#include "HashPassword.h"
#include "KeyStorage.h"
using android::security::keystore::IKeystoreService;
using keystore::KeystoreResponsePromise;
using keystore::OperationResultPromise;
using android::security::keymaster::OperationResult;
// Store main DE raw ref / policy
extern std::string de_raw_ref;
extern std::map<userid_t, std::string> s_de_key_raw_refs;
extern std::map<userid_t, std::string> s_ce_key_raw_refs;
inline std::string hidlVec2String(const ::keystore::hidl_vec<uint8_t>& value) {
return std::string(reinterpret_cast<const std::string::value_type*>(&value[0]), value.size());
}
static bool lookup_ref_key_internal(std::map<userid_t, std::string>& key_map, const uint8_t* policy, userid_t* user_id) {
char policy_string_hex[FS_KEY_DESCRIPTOR_SIZE_HEX];
char key_map_hex[FS_KEY_DESCRIPTOR_SIZE_HEX];
policy_to_hex(policy, policy_string_hex);
for (std::map<userid_t, std::string>::iterator it=key_map.begin(); it!=key_map.end(); ++it) {
policy_to_hex(reinterpret_cast<const uint8_t*>(&it->second[0]), key_map_hex);
std::string key_map_hex_string = std::string(key_map_hex);
if (key_map_hex_string == policy_string_hex) {
*user_id = it->first;
return true;
}
}
return false;
}
extern "C" bool lookup_ref_key(const uint8_t* policy, uint8_t* policy_type) {
userid_t user_id = 0;
char policy_string_hex[FS_KEY_DESCRIPTOR_SIZE_HEX];
char de_raw_ref_hex[FS_KEY_DESCRIPTOR_SIZE_HEX];
policy_to_hex(policy, policy_string_hex);
policy_to_hex(reinterpret_cast<const uint8_t*>(&de_raw_ref[0]), de_raw_ref_hex);
std::string de_raw_ref_hex_string = std::string(de_raw_ref_hex);
std::string policy_type_string;
if (policy_string_hex == de_raw_ref_hex_string) {
policy_type_string = "0DK";
memcpy(policy_type, policy_type_string.data(), policy_type_string.size());
return true;
}
if (!lookup_ref_key_internal(s_de_key_raw_refs, policy, &user_id)) {
if (!lookup_ref_key_internal(s_ce_key_raw_refs, policy, &user_id)) {
return false;
} else
policy_type_string = "0CE" + std::to_string(user_id);
} else
policy_type_string = "0DE" + std::to_string(user_id);
memcpy(policy_type, policy_type_string.data(), policy_type_string.size());
return true;
}
extern "C" bool lookup_ref_tar(const uint8_t* policy_type, uint8_t* policy) {
std::string policy_type_string = std::string((char *) policy_type);
char policy_hex[FS_KEY_DESCRIPTOR_SIZE_HEX];
policy_to_hex(policy_type, policy_hex);
// Current encryption fscrypt policy is v1 (which is stored as version 0e)
if (policy_type_string.substr(0,1) != "0") {
printf("Unexpected version %c\n", policy_type[0]);
return false;
}
if (policy_type_string.substr(1, 2) == "DK") {
memcpy(policy, de_raw_ref.data(), de_raw_ref.size());
return true;
}
userid_t user_id = atoi(policy_type_string.substr(3, 4).c_str());
std::string raw_ref;
if (policy_type_string.substr(1, 1) == "D") {
if (lookup_key_ref(s_de_key_raw_refs, user_id, &raw_ref)) {
memcpy(policy, raw_ref.data(), raw_ref.size());
} else
return false;
} else if (policy_type_string.substr(1, 1) == "C") {
if (lookup_key_ref(s_ce_key_raw_refs, user_id, &raw_ref)) {
memcpy(policy, raw_ref.data(), raw_ref.size());
} else
return false;
} else {
printf("unknown policy type '%s'\n", policy_type);
return false;
}
return true;
}
bool Decrypt_DE() {
if (!fscrypt_initialize_systemwide_keys()) { // this deals with the overarching device encryption
printf("fscrypt_initialize_systemwide_keys returned fail\n");
return false;
}
if (!fscrypt_init_user0()) {
printf("fscrypt_init_user0 returned fail\n");
return false;
}
return true;
}
// Crappy functions for debugging, please ignore unless you need to debug
// void output_hex(const std::string& in) {
// const char *buf = in.data();
// char hex[in.size() * 2 + 1];
// unsigned int index;
// for (index = 0; index < in.size(); index++)
// sprintf(&hex[2 * index], "%02X", buf[index]);
// printf("%s", hex);
// }
// void output_hex(const char* buf, const int size) {
// char hex[size * 2 + 1];
// int index;
// for (index = 0; index < size; index++)
// sprintf(&hex[2 * index], "%02X", buf[index]);
// printf("%s", hex);
// }
// void output_hex(const unsigned char* buf, const int size) {
// char hex[size * 2 + 1];
// int index;
// for (index = 0; index < size; index++)
// sprintf(&hex[2 * index], "%02X", buf[index]);
// printf("%s", hex);
// }
// void output_hex(std::vector<uint8_t>* vec) {
// char hex[3];
// unsigned int index;
// for (index = 0; index < vec->size(); index++) {
// sprintf(&hex[0], "%02X", vec->at(index));
// printf("%s", hex);
// }
// }
/* An alternative is to use:
* sqlite3 /data/system/locksettings.db "SELECT value FROM locksettings WHERE name='sp-handle' AND user=0;"
* but we really don't want to include the 1.1MB libsqlite in TWRP. We scan the spblob folder for the
* password data file (*.pwd) and get the handle from the filename instead. This is a replacement for
* https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/LockSettingsService.java#2017
* We never use this data as an actual long. We always use it as a string. */
bool Find_Handle(const std::string& spblob_path, std::string& handle_str) {
DIR* dir = opendir(spblob_path.c_str());
if (!dir) {
printf("Error opening '%s'\n", spblob_path.c_str());
return false;
}
struct dirent* de = 0;
while ((de = readdir(dir)) != 0) {
if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
continue;
size_t len = strlen(de->d_name);
if (len <= 4)
continue;
char* p = de->d_name;
p += len - 4;
if (strncmp(p, ".pwd", 4) == 0) {
handle_str = de->d_name;
handle_str = handle_str.substr(0, len - 4);
//*handle = strtoull(handle_str.c_str(), 0 , 16);
closedir(dir);
return true;
}
}
closedir(dir);
return false;
}
/* This is the structure of the data in the password data (*.pwd) file which the structure can be found
* https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#187 */
struct password_data_struct {
int password_type;
unsigned char scryptN;
unsigned char scryptR;
unsigned char scryptP;
int salt_len;
void* salt;
int handle_len;
void* password_handle;
};
/* C++ replacement for
* https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#764 */
bool Get_Password_Data(const std::string& spblob_path, const std::string& handle_str, password_data_struct *pwd) {
std::string pwd_file = spblob_path + handle_str + ".pwd";
std::string pwd_data;
if (!android::base::ReadFileToString(pwd_file, &pwd_data)) {
printf("Failed to read '%s'\n", pwd_file.c_str());
return false;
}
// output_hex(pwd_data.data(), pwd_data.size());printf("\n");
const int* intptr = (const int*)pwd_data.data();
pwd->password_type = *intptr;
endianswap(&pwd->password_type);
//printf("password type %i\n", pwd->password_type); // 2 was PIN, 1 for pattern, 2 also for password, -1 for default password
const unsigned char* byteptr = (const unsigned char*)pwd_data.data() + sizeof(int);
pwd->scryptN = *byteptr;
byteptr++;
pwd->scryptR = *byteptr;
byteptr++;
pwd->scryptP = *byteptr;
byteptr++;
intptr = (const int*)byteptr;
pwd->salt_len = *intptr;
endianswap(&pwd->salt_len);
if (pwd->salt_len != 0) {
pwd->salt = malloc(pwd->salt_len);
if (!pwd->salt) {
printf("Get_Password_Data malloc salt\n");
return false;
}
memcpy(pwd->salt, intptr + 1, pwd->salt_len);
intptr++;
byteptr = (const unsigned char*)intptr;
byteptr += pwd->salt_len;
} else {
printf("Get_Password_Data salt_len is 0\n");
return false;
}
intptr = (const int*)byteptr;
pwd->handle_len = *intptr;
endianswap(&pwd->handle_len);
if (pwd->handle_len != 0) {
pwd->password_handle = malloc(pwd->handle_len);
if (!pwd->password_handle) {
printf("Get_Password_Data malloc password_handle\n");
return false;
}
memcpy(pwd->password_handle, intptr + 1, pwd->handle_len);
} else {
printf("Get_Password_Data handle_len is 0\n");
// Not an error if using weaver
}
return true;
}
/* C++ replacement for
* https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#765
* called here
* https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#1050 */
bool Get_Password_Token(const password_data_struct *pwd, const std::string& Password, unsigned char* password_token) {
if (!password_token) {
printf("password_token is null\n");
return false;
}
unsigned int N = 1 << pwd->scryptN;
unsigned int r = 1 << pwd->scryptR;
unsigned int p = 1 << pwd->scryptP;
//printf("N %i r %i p %i\n", N, r, p);
int ret = crypto_scrypt(reinterpret_cast<const uint8_t*>(Password.data()), Password.size(),
reinterpret_cast<const uint8_t*>(pwd->salt), pwd->salt_len,
N, r, p,
password_token, 32);
if (ret != 0) {
printf("scrypt error\n");
return false;
}
return true;
}
// Data structure for the *.weaver file, see Get_Weaver_Data below
struct weaver_data_struct {
unsigned char version;
int slot;
};
/* C++ replacement for
* https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#501
* called here
* https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#768 */
bool Get_Weaver_Data(const std::string& spblob_path, const std::string& handle_str, weaver_data_struct *wd) {
std::string weaver_file = spblob_path + handle_str + ".weaver";
std::string weaver_data;
if (!android::base::ReadFileToString(weaver_file, &weaver_data)) {
printf("Failed to read '%s'\n", weaver_file.c_str());
return false;
}
// output_hex(weaver_data.data(), weaver_data.size());printf("\n");
const unsigned char* byteptr = (const unsigned char*)weaver_data.data();
wd->version = *byteptr;
// printf("weaver version %i\n", wd->version);
const int* intptr = (const int*)weaver_data.data() + sizeof(unsigned char);
wd->slot = *intptr;
//endianswap(&wd->slot); not needed
// printf("weaver slot %i\n", wd->slot);
return true;
}
namespace android {
// On Android 8.0 for some reason init can't seem to completely stop keystore
// so we have to kill it too if it doesn't die on its own.
static void kill_keystore() {
DIR* dir = opendir("/proc");
if (dir) {
struct dirent* de = 0;
while ((de = readdir(dir)) != 0) {
if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
continue;
int pid = -1;
int ret = sscanf(de->d_name, "%d", &pid);
if (ret == 1) {
char cmdpath[PATH_MAX];
sprintf(cmdpath, "/proc/%d/cmdline", pid);
FILE* file = fopen(cmdpath, "r");
size_t task_size = PATH_MAX;
char task[PATH_MAX];
char* p = task;
if (getline(&p, &task_size, file) > 0) {
if (strstr(task, "keystore") != 0) {
printf("keystore pid %d found, sending kill.\n", pid);
kill(pid, SIGINT);
usleep(5000);
kill(pid, SIGKILL);
}
}
fclose(file);
}
}
closedir(dir);
}
}
// The keystore holds a file open on /data so we have to stop / kill it
// if we want to be able to unmount /data for things like formatting.
static void stop_keystore() {
printf("Stopping keystore...\n");
property_set("ctl.stop", "keystore");
usleep(5000);
kill_keystore();
}
/* These next 2 functions try to get the keystore service 50 times because
* the keystore is not always ready when TWRP boots */
android::sp<IBinder> getKeystoreBinder() {
android::sp<IServiceManager> sm = android::defaultServiceManager();
return sm->getService(String16("android.security.keystore"));
}
android::sp<IBinder> getKeystoreBinderRetry() {
printf("Starting keystore...\n");
property_set("ctl.start", "keystore");
int retry_count = 50;
android::sp<IBinder> binder = getKeystoreBinder();
while (binder == NULL && retry_count) {
printf("Waiting for keystore service... %i\n", retry_count--);
sleep(1);
binder = getKeystoreBinder();
}
return binder;
}
namespace keystore {
#define SYNTHETIC_PASSWORD_VERSION_V1 1
#define SYNTHETIC_PASSWORD_VERSION_V2 2
#define SYNTHETIC_PASSWORD_VERSION_V3 3
#define SYNTHETIC_PASSWORD_PASSWORD_BASED 0
#define SYNTHETIC_PASSWORD_KEY_PREFIX "USRSKEY_synthetic_password_"
#define USR_PRIVATE_KEY_PREFIX "USRPKEY_synthetic_password_"
static std::string mKey_Prefix;
/* The keystore alias subid is sometimes the same as the handle, but not always.
* In the case of handle 0c5303fd2010fe29, the alias subid used c5303fd2010fe29
* without the leading 0. We could try to parse the data from a previous
* keystore request, but I think this is an easier solution because there
* is little to no documentation on the format of data we get back from
* the keystore in this instance. We also want to copy everything to a temp
* folder so that any key upgrades that might take place do not actually
* upgrade the keys on the data partition. We rename all 1000 uid files to 0
* to pass the keystore permission checks. */
bool Find_Keystore_Alias_SubID_And_Prep_Files(const userid_t user_id, std::string& keystoreid, const std::string& handle_str) {
char path_c[PATH_MAX];
sprintf(path_c, "/data/misc/keystore/user_%d", user_id);
char user_dir[PATH_MAX];
sprintf(user_dir, "user_%d", user_id);
std::string source_path = "/data/misc/keystore/";
source_path += user_dir;
std::string handle_sub = handle_str;
while (handle_sub.substr(0,1) == "0") {
std::string temp = handle_sub.substr(1);
handle_sub = temp;
}
mKey_Prefix = "";
mkdir("/tmp/misc", 0755);
mkdir("/tmp/misc/keystore", 0755);
std::string destination_path = "/tmp/misc/keystore/";
destination_path += user_dir;
if (mkdir(destination_path.c_str(), 0755) && errno != EEXIST) {
printf("failed to mkdir '%s' %s\n", destination_path.c_str(), strerror(errno));
return false;
}
destination_path += "/";
DIR* dir = opendir(source_path.c_str());
if (!dir) {
printf("Error opening '%s'\n", source_path.c_str());
return false;
}
source_path += "/";
struct dirent* de = 0;
size_t prefix_len = strlen(SYNTHETIC_PASSWORD_KEY_PREFIX);
bool found_subid = false;
bool has_pkey = false; // PKEY has priority over SKEY
while ((de = readdir(dir)) != 0) {
if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
continue;
if (!found_subid) {
size_t len = strlen(de->d_name);
if (len <= prefix_len)
continue;
if (strstr(de->d_name, SYNTHETIC_PASSWORD_KEY_PREFIX) && !has_pkey)
mKey_Prefix = SYNTHETIC_PASSWORD_KEY_PREFIX;
else if (strstr(de->d_name, USR_PRIVATE_KEY_PREFIX)) {
mKey_Prefix = USR_PRIVATE_KEY_PREFIX;
has_pkey = true;
} else
continue;
if (strstr(de->d_name, handle_sub.c_str())) {
keystoreid = handle_sub;
printf("keystoreid matched handle_sub: '%s'\n", keystoreid.c_str());
found_subid = true;
} else {
std::string file = de->d_name;
std::size_t found = file.find_last_of("_");
if (found != std::string::npos) {
keystoreid = file.substr(found + 1);
// printf("possible keystoreid: '%s'\n", keystoreid.c_str());
//found_subid = true; // we'll keep going in hopes that we find a pkey or a match to the handle_sub
}
}
}
std::string src = source_path;
src += de->d_name;
std::ifstream srcif(src.c_str(), std::ios::binary);
std::string dst = destination_path;
dst += de->d_name;
std::size_t source_uid = dst.find("1000");
if (source_uid != std::string::npos)
dst.replace(source_uid, 4, "0");
std::ofstream dstof(dst.c_str(), std::ios::binary);
printf("copying '%s' to '%s'\n", src.c_str(), dst.c_str());
dstof << srcif.rdbuf();
srcif.close();
dstof.close();
}
closedir(dir);
if (!found_subid && !mKey_Prefix.empty() && !keystoreid.empty())
found_subid = true;
return found_subid;
}
/* C++ replacement for function of the same name
* https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#867
* returning an empty string indicates an error */
std::string unwrapSyntheticPasswordBlob(const std::string& spblob_path, const std::string& handle_str, const userid_t user_id,
const void* application_id, const size_t application_id_size, uint32_t auth_token_len) {
std::string disk_decryption_secret_key = "";
android::ProcessState::self()->startThreadPool();
std::string keystore_alias_subid;
if (!Find_Keystore_Alias_SubID_And_Prep_Files(user_id, keystore_alias_subid, handle_str)) {
printf("failed to scan keystore alias subid and prep keystore files\n");
return disk_decryption_secret_key;
}
// First get the keystore service
android::sp<IBinder> binder = getKeystoreBinderRetry();
android::sp<IKeystoreService> service = interface_cast<IKeystoreService>(binder);
if (service == NULL) {
printf("error: could not connect to keystore service\n");
return disk_decryption_secret_key;
}
if (auth_token_len > 0) {
printf("Starting keystore_auth service...\n");
property_set("ctl.start", "keystore_auth");
}
// Read the data from the .spblob file per: https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#869
std::string spblob_file = spblob_path + handle_str + ".spblob";
std::string spblob_data;
if (!android::base::ReadFileToString(spblob_file, &spblob_data)) {
printf("Failed to read '%s'\n", spblob_file.c_str());
return disk_decryption_secret_key;
}
unsigned char* byteptr = (unsigned char*)spblob_data.data();
if (*byteptr != SYNTHETIC_PASSWORD_VERSION_V2 && *byteptr != SYNTHETIC_PASSWORD_VERSION_V1
&& *byteptr != SYNTHETIC_PASSWORD_VERSION_V3) {
printf("Unsupported synthetic password version %i\n", *byteptr);
return disk_decryption_secret_key;
}
const unsigned char* synthetic_password_version = byteptr;
byteptr++;
if (*byteptr != SYNTHETIC_PASSWORD_PASSWORD_BASED) {
printf("spblob data is not SYNTHETIC_PASSWORD_PASSWORD_BASED\n");
return disk_decryption_secret_key;
}
byteptr++; // Now we're pointing to the blob data itself
if (*synthetic_password_version == SYNTHETIC_PASSWORD_VERSION_V2
|| *synthetic_password_version == SYNTHETIC_PASSWORD_VERSION_V3) {
printf("spblob v2 / v3\n");
/* Version 2 / 3 of the spblob is basically the same as version 1, but the order of getting the intermediate key and disk decryption key have been flip-flopped
* as seen in https://android.googlesource.com/platform/frameworks/base/+/5025791ac6d1538224e19189397de8d71dcb1a12
*/
/* First decrypt call found in
* https://android.googlesource.com/platform/frameworks/base/+/android-8.1.0_r18/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java#135
* We will use https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java
* and https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/keystore/java/android/security/keystore/AndroidKeyStoreAuthenticatedAESCipherSpi.java
* First we set some algorithm parameters as seen in two places:
* https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/keystore/java/android/security/keystore/AndroidKeyStoreAuthenticatedAESCipherSpi.java#297
* https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/keystore/java/android/security/keystore/AndroidKeyStoreAuthenticatedAESCipherSpi.java#216 */
// When using secdis (aka not weaver) you must supply an auth token to the keystore prior to the begin operation
if (auth_token_len > 0) {
/*::keystore::KeyStoreServiceReturnCode auth_result = service->addAuthToken(auth_token, auth_token_len);
if (!auth_result.isOk()) {
// The keystore checks the uid of the calling process and will return a permission denied on this operation for user 0
printf("keystore error adding auth token\n");
return disk_decryption_secret_key;
}*/
// The keystore refuses to allow the root user to supply auth tokens, so we write the auth token to a file earlier and
// run a separate service that runs user the system user to add the auth token. We wait for the auth token file to be
// deleted by the keymaster_auth service and check for a /auth_error file in case of errors. We quit after after a while if
// the /auth_token file never gets deleted.
int auth_wait_count = 20;
while (access("/auth_token", F_OK) == 0 && auth_wait_count-- > 0)
usleep(5000);
if (auth_wait_count == 0 || access("/auth_error", F_OK) == 0) {
printf("error during keymaster_auth service\n");
/* If you are getting this error, make sure that you have the keymaster_auth service defined in your init scripts, preferrably in init.recovery.{ro.hardware}.rc
* service keystore_auth /system/bin/keystore_auth
* disabled
* oneshot
* user system
* group root
* seclabel u:r:recovery:s0
*
* And check dmesg for error codes regarding this service if needed. */
return disk_decryption_secret_key;
}
}
int32_t ret;
size_t maclen = 128;
unsigned char* iv = (unsigned char*)byteptr; // The IV is the first 12 bytes of the spblob
::keystore::hidl_vec<uint8_t> iv_hidlvec;
iv_hidlvec.setToExternal((unsigned char*)byteptr, 12);
// printf("iv: "); output_hex((const unsigned char*)iv, 12); printf("\n");
std::string keystore_alias = mKey_Prefix;
keystore_alias += keystore_alias_subid;
String16 keystore_alias16(keystore_alias.data(), keystore_alias.size());
int32_t error_code;
unsigned char* cipher_text = (unsigned char*)byteptr + 12; // The cipher text comes immediately after the IV
std::string cipher_text_str(byteptr, byteptr + spblob_data.size() - 14);
::keystore::hidl_vec<uint8_t> cipher_text_hidlvec;
::keystore::AuthorizationSetBuilder begin_params;
cipher_text_hidlvec.setToExternal(cipher_text, spblob_data.size() - 14 /* 1 each for version and SYNTHETIC_PASSWORD_PASSWORD_BASED and 12 for the iv */);
begin_params.Authorization(::keystore::TAG_ALGORITHM, ::keystore::Algorithm::AES);
begin_params.Authorization(::keystore::TAG_BLOCK_MODE, ::keystore::BlockMode::GCM);
begin_params.Padding(::keystore::PaddingMode::NONE);
begin_params.Authorization(::keystore::TAG_NONCE, iv_hidlvec);
begin_params.Authorization(::keystore::TAG_MAC_LENGTH, maclen);
::keystore::hidl_vec<uint8_t> entropy; // No entropy is needed for decrypt
entropy.resize(0);
android::security::keymaster::KeymasterArguments empty_params;
android::hardware::keymaster::V4_0::KeyPurpose decryptPurpose = android::hardware::keymaster::V4_0::KeyPurpose::DECRYPT;
android::sp<android::IBinder> decryptAuthToken(new android::BBinder);
android::sp<OperationResultPromise> promise = new OperationResultPromise;
auto future = promise->get_future();
auto binder_result = service->begin(promise, decryptAuthToken, keystore_alias16, (int32_t)decryptPurpose, true,
android::security::keymaster::KeymasterArguments(begin_params.hidl_data()),
entropy, -1, &error_code);
if (!binder_result.isOk()) {
printf("communication error while calling keystore\n");
return disk_decryption_secret_key;
}
::keystore::KeyStoreNativeReturnCode rc(error_code);
if (!rc.isOk()) {
printf("Keystore begin returned: %u\n", error_code);
return disk_decryption_secret_key;
}
OperationResult result = future.get();
auto handle = std::move(result.token);
// The cipher.doFinal call triggers an update to the keystore followed by a finish https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java#64
// See also https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java#208
future = {};
promise = new OperationResultPromise();
future = promise->get_future();
binder_result = service->update(promise, handle, empty_params, cipher_text_hidlvec, &error_code);
rc = ::keystore::KeyStoreNativeReturnCode(error_code);
if (!rc.isOk()) {
printf("Keystore update returned: %d\n", error_code);
return disk_decryption_secret_key;
}
result = future.get();
if (!result.resultCode.isOk()) {
printf("update failed: %d\n", error_code);
return disk_decryption_secret_key;
}
size_t keystore_result_size = result.data.size();
unsigned char* keystore_result = (unsigned char*)malloc(keystore_result_size);
if (!keystore_result) {
printf("malloc on keystore_result\n");
return disk_decryption_secret_key;
}
memcpy(keystore_result, &result.data[0], result.data.size());
future = {};
promise = new OperationResultPromise();
future = promise->get_future();
::keystore::hidl_vec<uint8_t> signature;
binder_result = service->finish(promise, handle, empty_params, signature, entropy, &error_code);
if (!binder_result.isOk()) {
printf("communication error while calling keystore\n");
free(keystore_result);
return disk_decryption_secret_key;
}
rc = ::keystore::KeyStoreNativeReturnCode(error_code);
if (!rc.isOk()) {
printf("Keystore finish returned: %d\n", error_code);
return disk_decryption_secret_key;
}
result = future.get();
if (!result.resultCode.isOk()) {
printf("finish failed: %d\n", error_code);
return disk_decryption_secret_key;
}
stop_keystore();
/* Now we do the second decrypt call as seen in:
* https://android.googlesource.com/platform/frameworks/base/+/android-8.1.0_r18/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java#136
*/
const unsigned char* intermediate_iv = keystore_result;
// printf("intermediate_iv: "); output_hex((const unsigned char*)intermediate_iv, 12); printf("\n");
const unsigned char* intermediate_cipher_text = (const unsigned char*)keystore_result + 12; // The cipher text comes immediately after the IV
int cipher_size = keystore_result_size - 12;
// First we personalize as seen https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java#102
void* personalized_application_id = PersonalizedHashBinary(PERSONALISATION_APPLICATION_ID, (const char*)application_id, application_id_size);
if (!personalized_application_id) {
return disk_decryption_secret_key;
}
// printf("personalized application id: "); output_hex((unsigned char*)personalized_application_id, SHA512_DIGEST_LENGTH); printf("\n");
// Now we'll decrypt using openssl AES/GCM/NoPadding
OpenSSL_add_all_ciphers();
int actual_size=0, final_size=0;
EVP_CIPHER_CTX *d_ctx = EVP_CIPHER_CTX_new();
const unsigned char* key = (const unsigned char*)personalized_application_id; // The key is the now personalized copy of the application ID
// printf("key: "); output_hex((const unsigned char*)key, 32); printf("\n");
EVP_DecryptInit(d_ctx, EVP_aes_256_gcm(), key, intermediate_iv);
unsigned char* secret_key = (unsigned char*)malloc(cipher_size);
if (!secret_key) {
printf("malloc failure on secret key\n");
return disk_decryption_secret_key;
}
EVP_DecryptUpdate(d_ctx, secret_key, &actual_size, intermediate_cipher_text, cipher_size);
unsigned char tag[AES_BLOCK_SIZE];
EVP_CIPHER_CTX_ctrl(d_ctx, EVP_CTRL_GCM_SET_TAG, 16, tag);
EVP_DecryptFinal_ex(d_ctx, secret_key + actual_size, &final_size);
EVP_CIPHER_CTX_free(d_ctx);
free(personalized_application_id);
free(keystore_result);
int secret_key_real_size = actual_size - 16;
// printf("secret key: "); output_hex((const unsigned char*)secret_key, secret_key_real_size); printf("\n");
// The payload data from the keystore update is further personalized at https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#153
// We now have the disk decryption key!
if (*synthetic_password_version == SYNTHETIC_PASSWORD_VERSION_V3) {
// V3 uses SP800 instead of SHA512
disk_decryption_secret_key = PersonalizedHashSP800(PERSONALIZATION_FBE_KEY, PERSONALISATION_CONTEXT, (const char*)secret_key, secret_key_real_size);
} else {
disk_decryption_secret_key = PersonalizedHash(PERSONALIZATION_FBE_KEY, (const char*)secret_key, secret_key_real_size);
}
// printf("disk_decryption_secret_key: '%s'\n", disk_decryption_secret_key.c_str());
free(secret_key);
return disk_decryption_secret_key;
}
return disk_decryption_secret_key;
}
}}
#define PASSWORD_TOKEN_SIZE 32
/* C++ replacement for
* https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#992
* called here
* https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#813 */
bool Get_Secdis(const std::string& spblob_path, const std::string& handle_str, std::string& secdis_data) {
std::string secdis_file = spblob_path + handle_str + ".secdis";
if (!android::base::ReadFileToString(secdis_file, &secdis_data)) {
printf("Failed to read '%s'\n", secdis_file.c_str());
return false;
}
// output_hex(secdis_data.data(), secdis_data.size());printf("\n");
return true;
}
// C++ replacement for https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#1033
userid_t fakeUid(const userid_t uid) {
return 100000 + uid;
}
bool Is_Weaver(const std::string& spblob_path, const std::string& handle_str) {
std::string weaver_file = spblob_path + handle_str + ".weaver";
struct stat st;
if (stat(weaver_file.c_str(), &st) == 0)
return true;
return false;
}
bool Free_Return(bool retval, void* weaver_key, password_data_struct* pwd) {
if (weaver_key)
free(weaver_key);
if (pwd->salt)
free(pwd->salt);
if (pwd->password_handle)
free(pwd->password_handle);
return retval;
}
/* Decrypt_User_Synth_Pass is the TWRP C++ equivalent to spBasedDoVerifyCredential
* https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/LockSettingsService.java#1998 */
bool Decrypt_User_Synth_Pass(const userid_t user_id, const std::string& Password) {
bool retval = false;
void* weaver_key = NULL;
password_data_struct pwd;
pwd.salt = NULL;
pwd.salt_len = 0;
pwd.password_handle = NULL;
pwd.handle_len = 0;
char application_id[PASSWORD_TOKEN_SIZE + SHA512_DIGEST_LENGTH];
uint32_t auth_token_len = 0;
std::string secret; // this will be the disk decryption key that is sent to vold
std::string token = "!"; // there is no token used for this kind of decrypt, key escrow is handled by weaver
int flags = FLAG_STORAGE_DE;
if (user_id == 0)
flags = FLAG_STORAGE_DE;
else
flags = FLAG_STORAGE_CE;
char spblob_path_char[PATH_MAX];
sprintf(spblob_path_char, "/data/system_de/%d/spblob/", user_id);
std::string spblob_path = spblob_path_char;
long handle = 0;
std::string handle_str;
// Get the handle: https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/LockSettingsService.java#2017
if (!Find_Handle(spblob_path, handle_str)) {
printf("Error getting handle\n");
return Free_Return(retval, weaver_key, &pwd);
}
// printf("Handle is '%s'\n", handle_str.c_str());
// Now we begin driving unwrapPasswordBasedSyntheticPassword from: https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#758
// First we read the password data which contains scrypt parameters
if (!Get_Password_Data(spblob_path, handle_str, &pwd)) {
printf("Failed to Get_Password_Data\n");
return Free_Return(retval, weaver_key, &pwd);
}
// printf("pwd N %i R %i P %i salt ", pwd.scryptN, pwd.scryptR, pwd.scryptP); output_hex((char*)pwd.salt, pwd.salt_len); printf("\n");
unsigned char password_token[PASSWORD_TOKEN_SIZE];
// printf("Password: '%s'\n", Password.c_str());
// The password token is the password scrypted with the parameters from the password data file
if (!Get_Password_Token(&pwd, Password, &password_token[0])) {
printf("Failed to Get_Password_Token\n");
return Free_Return(retval, weaver_key, &pwd);
}
// output_hex(&password_token[0], PASSWORD_TOKEN_SIZE);printf("\n");
if (Is_Weaver(spblob_path, handle_str)) {
printf("using weaver\n");
// BEGIN PIXEL 2 WEAVER
// Get the weaver data from the .weaver file which tells us which slot to use when we ask weaver for the escrowed key
// https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#768
weaver_data_struct wd;
if (!Get_Weaver_Data(spblob_path, handle_str, &wd)) {
printf("Failed to get weaver data\n");
return Free_Return(retval, weaver_key, &pwd);
}
// The weaver key is the the password token prefixed with "weaver-key" padded to 128 with nulls with the password token appended then SHA512
// https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#1059
weaver_key = PersonalizedHashBinary(PERSONALISATION_WEAVER_KEY, (char*)&password_token[0], PASSWORD_TOKEN_SIZE);
if (!weaver_key) {
printf("malloc error getting weaver_key\n");
return Free_Return(retval, weaver_key, &pwd);
}
// Now we start driving weaverVerify: https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#343
// Called from https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#776
android::vold::Weaver weaver;
if (!weaver) {
printf("Failed to get weaver service\n");
return Free_Return(retval, weaver_key, &pwd);
}
// Get the key size from weaver service
uint32_t weaver_key_size = 0;
if (!weaver.GetKeySize(&weaver_key_size)) {
printf("Failed to get weaver key size\n");
return Free_Return(retval, weaver_key, &pwd);
} else {
printf("weaver key size is %u\n", weaver_key_size);
}
// printf("weaver key: "); output_hex((unsigned char*)weaver_key, weaver_key_size); printf("\n");
// Send the slot from the .weaver file, the computed weaver key, and get the escrowed key data
std::vector<uint8_t> weaver_payload;
// TODO: we should return more information about the status including time delays before the next retry
if (!weaver.WeaverVerify(wd.slot, weaver_key, &weaver_payload)) {
printf("failed to weaver verify\n");
return Free_Return(retval, weaver_key, &pwd);
}
// printf("weaver payload: "); output_hex(&weaver_payload); printf("\n");
// Done with weaverVerify
// Now we will compute the application ID
// https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#964
// Called from https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#780
// The escrowed weaver key data is prefixed with "weaver-pwd" padded to 128 with nulls with the weaver payload appended then SHA512
void* weaver_secret = PersonalizedHashBinary(PERSONALISATION_WEAVER_PASSWORD, (const char*)weaver_payload.data(), weaver_payload.size());
// printf("weaver secret: "); output_hex((unsigned char*)weaver_secret, SHA512_DIGEST_LENGTH); printf("\n");
// The application ID is the password token and weaver secret appended to each other
memcpy((void*)&application_id[0], (void*)&password_token[0], PASSWORD_TOKEN_SIZE);
memcpy((void*)&application_id[PASSWORD_TOKEN_SIZE], weaver_secret, SHA512_DIGEST_LENGTH);
// printf("application ID: "); output_hex((unsigned char*)application_id, PASSWORD_TOKEN_SIZE + SHA512_DIGEST_LENGTH); printf("\n");
// END PIXEL 2 WEAVER
} else {
printf("using secdis\n");
std::string secdis_data;
if (!Get_Secdis(spblob_path, handle_str, secdis_data)) {
printf("Failed to get secdis data\n");
return Free_Return(retval, weaver_key, &pwd);
}
void* secdiscardable = PersonalizedHashBinary(PERSONALISATION_SECDISCARDABLE, (char*)secdis_data.data(), secdis_data.size());
if (!secdiscardable) {
printf("malloc error getting secdiscardable\n");
return Free_Return(retval, weaver_key, &pwd);
}
memcpy((void*)&application_id[0], (void*)&password_token[0], PASSWORD_TOKEN_SIZE);
memcpy((void*)&application_id[PASSWORD_TOKEN_SIZE], secdiscardable, SHA512_DIGEST_LENGTH);
int ret = -1;
bool request_reenroll = false;
android::sp<android::hardware::gatekeeper::V1_0::IGatekeeper> gk_device;
gk_device = ::android::hardware::gatekeeper::V1_0::IGatekeeper::getService();
if (gk_device == nullptr) {
printf("failed to get gatekeeper service\n");
return Free_Return(retval, weaver_key, &pwd);
}
if (pwd.handle_len <= 0) {
printf("no password handle supplied\n");
return Free_Return(retval, weaver_key, &pwd);
}
android::hardware::hidl_vec<uint8_t> pwd_handle_hidl;
pwd_handle_hidl.setToExternal(const_cast<uint8_t *>((const uint8_t *)pwd.password_handle), pwd.handle_len);
void* gk_pwd_token = PersonalizedHashBinary(PERSONALIZATION_USER_GK_AUTH, (char*)&password_token[0], PASSWORD_TOKEN_SIZE);
if (!gk_pwd_token) {
printf("malloc error getting gatekeeper_key\n");
return Free_Return(retval, weaver_key, &pwd);
}
android::hardware::hidl_vec<uint8_t> gk_pwd_token_hidl;
gk_pwd_token_hidl.setToExternal(const_cast<uint8_t *>((const uint8_t *)gk_pwd_token), SHA512_DIGEST_LENGTH);
android::hardware::Return<void> hwRet =
gk_device->verify(fakeUid(user_id), 0 /* challange */,
pwd_handle_hidl,
gk_pwd_token_hidl,
[&ret, &request_reenroll, &auth_token_len]
(const android::hardware::gatekeeper::V1_0::GatekeeperResponse &rsp) {
ret = static_cast<int>(rsp.code); // propagate errors
if (rsp.code >= android::hardware::gatekeeper::V1_0::GatekeeperStatusCode::STATUS_OK) {
auth_token_len = rsp.data.size();
request_reenroll = (rsp.code == android::hardware::gatekeeper::V1_0::GatekeeperStatusCode::STATUS_REENROLL);
ret = 0; // all success states are reported as 0
// The keystore refuses to allow the root user to supply auth tokens, so we write the auth token to a file here and later
// run a separate service that runs as the system user to add the auth token. We wait for the auth token file to be
// deleted by the keymaster_auth service and check for a /auth_error file in case of errors. We quit after a while seconds if
// the /auth_token file never gets deleted.
unlink("/auth_token");
FILE* auth_file = fopen("/auth_token","wb");
if (auth_file != NULL) {
fwrite(rsp.data.data(), sizeof(uint8_t), rsp.data.size(), auth_file);
fclose(auth_file);
} else {
printf("failed to open /auth_token for writing\n");
ret = -2;
}
} else if (rsp.code == android::hardware::gatekeeper::V1_0::GatekeeperStatusCode::ERROR_RETRY_TIMEOUT && rsp.timeout > 0) {
ret = rsp.timeout;
}
}
);
free(gk_pwd_token);
if (!hwRet.isOk() || ret != 0) {
printf("gatekeeper verification failed\n");
return Free_Return(retval, weaver_key, &pwd);
}
}
// Now we will handle https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#816
// Plus we will include the last bit that computes the disk decrypt key found in:
// https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#153
secret = android::keystore::unwrapSyntheticPasswordBlob(spblob_path, handle_str, user_id, (const void*)&application_id[0],
PASSWORD_TOKEN_SIZE + SHA512_DIGEST_LENGTH, auth_token_len);
if (!secret.size()) {
printf("failed to unwrapSyntheticPasswordBlob\n");
return Free_Return(retval, weaver_key, &pwd);
}
if (!fscrypt_unlock_user_key(user_id, 0, token.c_str(), secret.c_str())) {
printf("fscrypt_unlock_user_key returned fail\n");
return Free_Return(retval, weaver_key, &pwd);
}
if (!fscrypt_prepare_user_storage("", user_id, 0, flags)) {
printf("failed to fscrypt_prepare_user_storage\n");
return Free_Return(retval, weaver_key, &pwd);
}
printf("Decrypted Successfully!\n");
retval = true;
return Free_Return(retval, weaver_key, &pwd);
}
int Get_Password_Type(const userid_t user_id, std::string& filename) {
struct stat st;
char spblob_path_char[PATH_MAX];
sprintf(spblob_path_char, "/data/system_de/%d/spblob/", user_id);
if (stat(spblob_path_char, &st) == 0) {
printf("Using synthetic password method\n");
std::string spblob_path = spblob_path_char;
std::string handle_str;
if (!Find_Handle(spblob_path, handle_str)) {
printf("Error getting handle\n");
return 0;
}
printf("Handle is '%s'\n", handle_str.c_str());
password_data_struct pwd;
if (!Get_Password_Data(spblob_path, handle_str, &pwd)) {
printf("Failed to Get_Password_Data\n");
return 0;
}
if (pwd.password_type == 1) { // In Android this means pattern
printf("password type: pattern\n");
return 2; // In TWRP this means pattern
}
else if (pwd.password_type == 2) { // In Android this means PIN or password
printf("password type: pin\n");
return 1; // In TWRP this means PIN or password
}
printf("using default password\n");
return 0; // We'll try the default password
}
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";
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_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;
if (Default_Password) {
if (!fscrypt_unlock_user_key(user_id, 0, "!", "!")) {
printf("unlock_user_key returned fail\n");
return false;
}
if (!fscrypt_prepare_user_storage("", user_id, 0, flags)) {
printf("failed to fscrypt_prepare_user_storage\n");
return false;
}
printf("Decrypted Successfully!\n");
return true;
}
if (stat("/data/system_de/0/spblob", &st) == 0) {
printf("Using synthetic password method\n");
return Decrypt_User_Synth_Pass(user_id, Password);
}
// 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;
bool request_reenroll = false;
android::sp<android::hardware::gatekeeper::V1_0::IGatekeeper> gk_device;
gk_device = ::android::hardware::gatekeeper::V1_0::IGatekeeper::getService();
if (gk_device == nullptr)
return false;
android::hardware::hidl_vec<uint8_t> curPwdHandle;
curPwdHandle.setToExternal(const_cast<uint8_t *>((const uint8_t *)handle.c_str()), st.st_size);
android::hardware::hidl_vec<uint8_t> enteredPwd;
enteredPwd.setToExternal(const_cast<uint8_t *>((const uint8_t *)Password.c_str()), Password.size());
android::hardware::Return<void> hwRet =
gk_device->verify(user_id, 0 /* challange */,
curPwdHandle,
enteredPwd,
[&ret, &request_reenroll, &auth_token, &auth_token_len]
(const android::hardware::gatekeeper::V1_0::GatekeeperResponse &rsp) {
ret = static_cast<int>(rsp.code); // propagate errors
if (rsp.code >= android::hardware::gatekeeper::V1_0::GatekeeperStatusCode::STATUS_OK) {
auth_token = new uint8_t[rsp.data.size()];
auth_token_len = rsp.data.size();
memcpy(auth_token, rsp.data.data(), auth_token_len);
request_reenroll = (rsp.code == android::hardware::gatekeeper::V1_0::GatekeeperStatusCode::STATUS_REENROLL);
ret = 0; // all success states are reported as 0
} else if (rsp.code == android::hardware::gatekeeper::V1_0::GatekeeperStatusCode::ERROR_RETRY_TIMEOUT && rsp.timeout > 0) {
ret = rsp.timeout;
}
}
);
if (!hwRet.isOk()) {
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 (!fscrypt_unlock_user_key(user_id, 0, token_hex, secret.c_str())) {
printf("fscrypt_unlock_user_key returned fail\n");
return false;
}
if (!fscrypt_prepare_user_storage("", user_id, 0, flags)) {
printf("failed to fscrypt_prepare_user_storage\n");
return false;
}
printf("Decrypted Successfully!\n");
return true;
}