summaryrefslogtreecommitdiffstats
path: root/crypto
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--crypto/ext4crypt/Android.mk12
-rw-r--r--crypto/ext4crypt/Decrypt.cpp786
-rw-r--r--crypto/ext4crypt/Ext4Crypt.cpp8
-rw-r--r--crypto/ext4crypt/HashPassword.cpp39
-rw-r--r--crypto/ext4crypt/HashPassword.h10
-rw-r--r--crypto/ext4crypt/KeyStorage3.cpp526
-rw-r--r--crypto/ext4crypt/KeyStorage3.h58
-rw-r--r--crypto/ext4crypt/Keymaster3.cpp324
-rw-r--r--crypto/ext4crypt/Keymaster3.h148
-rw-r--r--crypto/ext4crypt/Utils.h8
-rw-r--r--crypto/ext4crypt/Weaver1.cpp128
-rw-r--r--crypto/ext4crypt/Weaver1.h64
-rw-r--r--crypto/ext4crypt/e4policyget.cpp9
-rw-r--r--crypto/ext4crypt/ext4_crypt.cpp61
-rw-r--r--crypto/ext4crypt/ext4crypt_tar.h20
15 files changed, 2154 insertions, 47 deletions
diff --git a/crypto/ext4crypt/Android.mk b/crypto/ext4crypt/Android.mk
index 8b1dcd4d8..b32f3841a 100644
--- a/crypto/ext4crypt/Android.mk
+++ b/crypto/ext4crypt/Android.mk
@@ -5,7 +5,7 @@ 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 ext4_crypt.cpp
+LOCAL_SRC_FILES := Decrypt.cpp Ext4Crypt.cpp ScryptParameters.cpp Utils.cpp HashPassword.cpp ext4_crypt.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 system/extras/ext4_utils/include/ext4_utils external/scrypt/lib/crypto system/security/keystore hardware/libhardware/include/hardware system/security/softkeymaster/include/keymaster system/keymaster/include
@@ -14,6 +14,16 @@ ifneq ($(wildcard hardware/libhardware/include/hardware/keymaster0.h),)
LOCAL_CFLAGS += -DTW_CRYPTO_HAVE_KEYMASTERX
LOCAL_C_INCLUDES += external/boringssl/src/include
endif
+ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 26; echo $$?),0)
+ LOCAL_CFLAGS += -DUSE_KEYSTORAGE_3 -DHAVE_LIBKEYUTILS -DHAVE_SYNTH_PWD_SUPPORT -DHAVE_GATEKEEPER1
+ LOCAL_SRC_FILES += Keymaster3.cpp KeyStorage3.cpp
+ LOCAL_SHARED_LIBRARIES += android.hardware.keymaster@3.0 libkeystore_binder libhidlbase libutils libkeyutils libbinder
+ LOCAL_SHARED_LIBRARIES += android.hardware.gatekeeper@1.0
+ LOCAL_SRC_FILES += Weaver1.cpp
+ LOCAL_SHARED_LIBRARIES += android.hardware.weaver@1.0
+else
+ LOCAL_SRC_FILES += Keymaster.cpp KeyStorage.cpp
+endif
include $(BUILD_SHARED_LIBRARY)
diff --git a/crypto/ext4crypt/Decrypt.cpp b/crypto/ext4crypt/Decrypt.cpp
index 3b69d4651..2dab16646 100644
--- a/crypto/ext4crypt/Decrypt.cpp
+++ b/crypto/ext4crypt/Decrypt.cpp
@@ -26,10 +26,50 @@
#include <sys/stat.h>
#include <sys/types.h>
-#include "ext4_crypt.h"
+#ifndef HAVE_LIBKEYUTILS
#include "key_control.h"
+#else
+#include <keyutils.h>
+#endif
+
+#ifdef HAVE_SYNTH_PWD_SUPPORT
+#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 <ext4_utils/ext4_crypt.h>
+
+#include <keystore/IKeystoreService.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+
+#include <keystore/keystore.h>
+#include <keystore/authorization_set.h>
+
+#include <algorithm>
+extern "C" {
+#include "crypto_scrypt.h"
+}
+#else
+#include "ext4_crypt.h"
+#endif //ifdef HAVE_SYNTH_PWD_SUPPORT
+#ifdef HAVE_GATEKEEPER1
+#include <android/hardware/gatekeeper/1.0/IGatekeeper.h>
+#else
#include <hardware/gatekeeper.h>
+#endif
#include "HashPassword.h"
#include <android-base/file.h>
@@ -67,7 +107,7 @@ extern "C" bool lookup_ref_key(const char* policy, char* policy_type) {
extern "C" bool lookup_ref_tar(const char* policy_type, char* policy) {
if (strncmp(policy_type, "1", 1) != 0) {
- printf("Unexpected version %c\n", policy_type);
+ printf("Unexpected version %c\n", policy_type[0]);
return false;
}
const char* ptr = policy_type + 1; // skip past the version number
@@ -94,6 +134,7 @@ extern "C" bool lookup_ref_tar(const char* policy_type, char* policy) {
return true;
}
+#ifndef HAVE_GATEKEEPER1
int gatekeeper_device_initialize(gatekeeper_device_t **dev) {
int ret;
const hw_module_t *mod;
@@ -110,8 +151,685 @@ int gatekeeper_device_initialize(gatekeeper_device_t **dev) {
printf("failed to open gatekeeper\n");
return ret;
}
+#endif //ifndef HAVE_GATEKEEPER1
+
+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;
+}
+
+#ifdef HAVE_SYNTH_PWD_SUPPORT
+// 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;
+}
+
+// The password data is stored in big endian and has to be swapped on little endian ARM
+template <class T>
+void endianswap(T *objp) {
+ unsigned char *memp = reinterpret_cast<unsigned char*>(objp);
+ std::reverse(memp, memp + sizeof(T));
+}
+
+/* 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);
+ } else {
+ printf("Get_Password_Data salt_len is 0\n");
+ return false;
+ }
+ 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 */
+sp<IBinder> getKeystoreBinder() {
+ sp<IServiceManager> sm = defaultServiceManager();
+ return sm->getService(String16("android.security.keystore"));
+}
+
+sp<IBinder> getKeystoreBinderRetry() {
+ printf("Starting keystore...\n");
+ property_set("ctl.start", "keystore");
+ int retry_count = 50;
+ 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 1
+#define SYNTHETIC_PASSWORD_PASSWORD_BASED 0
+#define SYNTHETIC_PASSWORD_KEY_PREFIX "USRSKEY_synthetic_password_"
+
+/* 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) {
+ 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;
+
+ 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;
+
+ 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))
+ continue;
+ std::string file = de->d_name;
+ std::size_t found = file.find_last_of("_");
+ if (found != std::string::npos) {
+ keystoreid = file.substr(found + 1);
+ printf("keystoreid: '%s'\n", keystoreid.c_str());
+ found_subid = true;
+ }
+ }
+ 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);
+ 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) {
+ std::string disk_decryption_secret_key = "";
+
+ std::string keystore_alias_subid;
+ if (!Find_Keystore_Alias_SubID_And_Prep_Files(user_id, keystore_alias_subid)) {
+ printf("failed to scan keystore alias subid and prep keystore files\n");
+ return disk_decryption_secret_key;
+ }
+
+ // First get the keystore service
+ sp<IBinder> binder = getKeystoreBinderRetry();
+ sp<IKeystoreService> service = interface_cast<IKeystoreService>(binder);
+ if (service == NULL) {
+ printf("error: could not connect to keystore service\n");
+ return disk_decryption_secret_key;
+ }
+
+ // 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;
+ }
+ const unsigned char* byteptr = (const unsigned char*)spblob_data.data();
+ if (*byteptr != SYNTHETIC_PASSWORD_VERSION) {
+ printf("SYNTHETIC_PASSWORD_VERSION does not match\n");
+ return disk_decryption_secret_key;
+ }
+ 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
+ /* We're now going to handle decryptSPBlob: https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java#115
+ * Called from https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java#879
+ * This small function ends up being quite a headache. The call to get data from the keystore basically is not needed in TWRP at this time.
+ * The keystore data seems to be the serialized data from an entire class in Java. Specifically I think it represents:
+ * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java
+ * or perhaps
+ * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/keystore/java/android/security/keystore/AndroidKeyStoreAuthenticatedAESCipherSpi.java
+ * but the only things we "need" from this keystore are a user ID and the keyAlias which ends up being USRSKEY_synthetic_password_{handle_str}
+ * the latter of which we already have. We may need to figure out how to get the user ID if we ever support decrypting mulitple users.
+ * There are 2 calls to a Java decrypt funcion that is overloaded. These 2 calls go in completely different directions despite the seemingly
+ * similar use of decrypt() and decrypt parameters. To figure out where things were going, I added logging to:
+ * https://android.googlesource.com/platform/libcore/+/android-8.0.0_r23/ojluni/src/main/java/javax/crypto/Cipher.java#2575
+ * Logger.global.severe("Cipher tryCombinations " + prov.getName() + " - " + prov.getInfo());
+ * To make logging work in libcore, import java.util.logging.Logger; and either set a better logging level or modify the framework to log everything
+ * regardless of logging level. This will give you some strings that you can grep for and find the actual crypto provider in use. In our case there were
+ * 2 different providers in use. The first stage to get the intermediate key used:
+ * https://android.googlesource.com/platform/external/conscrypt/+/android-8.0.0_r23/common/src/main/java/org/conscrypt/OpenSSLProvider.java
+ * which is a pretty straight-forward OpenSSL implementation of AES/GCM/NoPadding. */
+ // 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) {
+ printf("malloc personalized_application_id\n");
+ 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* iv = (const unsigned char*)byteptr; // The IV is the first 12 bytes of the spblob
+ //printf("iv: "); output_hex((const unsigned char*)iv, 12); printf("\n");
+ const unsigned char* cipher_text = (const unsigned char*)byteptr + 12; // The cipher text comes immediately after the IV
+ //printf("cipher_text: "); output_hex((const unsigned char*)cipher_text, spblob_data.size() - 2 - 12); printf("\n");
+ 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, iv);
+ std::vector<unsigned char> intermediate_key;
+ intermediate_key.resize(spblob_data.size() - 2 - 12, '\0');
+ EVP_DecryptUpdate(d_ctx, &intermediate_key[0], &actual_size, cipher_text, spblob_data.size() - 2 - 12);
+ unsigned char tag[AES_BLOCK_SIZE];
+ EVP_CIPHER_CTX_ctrl(d_ctx, EVP_CTRL_GCM_SET_TAG, 16, tag);
+ EVP_DecryptFinal_ex(d_ctx, &intermediate_key[actual_size], &final_size);
+ EVP_CIPHER_CTX_free(d_ctx);
+ free(personalized_application_id);
+ //printf("spblob_data size: %lu actual_size %i, final_size: %i\n", spblob_data.size(), actual_size, final_size);
+ intermediate_key.resize(actual_size + final_size - 16, '\0');// not sure why we have to trim the size by 16 as I don't see where this is done in Java side
+ //printf("intermediate key: "); output_hex((const unsigned char*)intermediate_key.data(), intermediate_key.size()); printf("\n");
+
+ int32_t ret;
+
+ /* We only need a keyAlias which is USRSKEY_synthetic_password_b6f71045af7bd042 which we find and a uid which is -1 or 1000, I forget which
+ * as the key data will be read again by the begin function later via the keystore.
+ * The data is in a hidl_vec format which consists of a type and a value. */
+ /*::keystore::hidl_vec<uint8_t> data;
+ std::string keystoreid = SYNTHETIC_PASSWORD_KEY_PREFIX;
+ keystoreid += handle_str;
+
+ ret = service->get(String16(keystoreid.c_str()), user_id, &data);
+ if (ret < 0) {
+ printf("Could not connect to keystore service %i\n", ret);
+ return disk_decryption_secret_key;
+ } else if (ret != 1 /*android::keystore::ResponseCode::NO_ERROR*//*) {
+ printf("keystore error: (%d)\n", /*responses[ret],*//* ret);
+ return disk_decryption_secret_key;
+ } else {
+ printf("keystore returned: "); output_hex(&data[0], data.size()); printf("\n");
+ }*/
+
+ // Now we'll break up the intermediate key into the IV (first 12 bytes) and the cipher text (the rest of it).
+ std::vector<unsigned char> nonce = intermediate_key;
+ nonce.resize(12);
+ intermediate_key.erase (intermediate_key.begin(),intermediate_key.begin()+12);
+ //printf("nonce: "); output_hex((const unsigned char*)nonce.data(), nonce.size()); printf("\n");
+ //printf("cipher text: "); output_hex((const unsigned char*)intermediate_key.data(), intermediate_key.size()); printf("\n");
+
+ /* Now we will begin the second decrypt call found in
+ * https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java#122
+ * This time 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 */
+ size_t maclen = 128;
+ ::keystore::AuthorizationSetBuilder begin_params;
+ 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, nonce);
+ begin_params.Authorization(::keystore::TAG_MAC_LENGTH, maclen);
+ //keymasterArgs.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_AES);
+ //keymasterArgs.addEnum(KeymasterDefs.KM_TAG_BLOCK_MODE, mKeymasterBlockMode);
+ //keymasterArgs.addEnum(KeymasterDefs.KM_TAG_PADDING, mKeymasterPadding);
+ //keymasterArgs.addUnsignedInt(KeymasterDefs.KM_TAG_MAC_LENGTH, mTagLengthBits);
+ ::keystore::hidl_vec<uint8_t> entropy; // No entropy is needed for decrypt
+ entropy.resize(0);
+ std::string keystore_alias = SYNTHETIC_PASSWORD_KEY_PREFIX;
+ keystore_alias += keystore_alias_subid;
+ String16 keystore_alias16(keystore_alias.c_str());
+ ::keystore::KeyPurpose purpose = ::keystore::KeyPurpose::DECRYPT;
+ OperationResult begin_result;
+ // These parameters are mostly driven by the cipher.init call https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java#63
+ service->begin(binder, keystore_alias16, purpose, true, begin_params.hidl_data(), entropy, -1, &begin_result);
+ ret = begin_result.resultCode;
+ if (ret != 1 /*android::keystore::ResponseCode::NO_ERROR*/) {
+ printf("keystore begin error: (%d)\n", /*responses[ret],*/ ret);
+ return disk_decryption_secret_key;
+ } else {
+ //printf("keystore begin operation successful\n");
+ }
+ ::keystore::hidl_vec<::keystore::KeyParameter> empty_params;
+ empty_params.resize(0);
+ OperationResult update_result;
+ // The cipher.doFinal call triggers an update to the keystore followed by a finish https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java#64
+ // See also https://android.googlesource.com/platform/frameworks/base/+/android-8.0.0_r23/keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java#208
+ service->update(begin_result.token, empty_params, intermediate_key, &update_result);
+ ret = update_result.resultCode;
+ if (ret != 1 /*android::keystore::ResponseCode::NO_ERROR*/) {
+ printf("keystore update error: (%d)\n", /*responses[ret],*/ ret);
+ return disk_decryption_secret_key;
+ } else {
+ //printf("keystore update operation successful\n");
+ //printf("keystore update returned: "); output_hex(&update_result.data[0], update_result.data.size()); printf("\n"); // this ends up being the synthetic password
+ }
+ // We must use the data in update_data.data before we call finish below or the data will be gone
+ // 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!
+ disk_decryption_secret_key = PersonalizedHash(PERSONALIZATION_FBE_KEY, (const char*)&update_result.data[0], update_result.data.size());
+ //printf("disk_decryption_secret_key: '%s'\n", disk_decryption_secret_key.c_str());
+ ::keystore::hidl_vec<uint8_t> signature;
+ OperationResult finish_result;
+ service->finish(begin_result.token, empty_params, signature, entropy, &finish_result);
+ ret = finish_result.resultCode;
+ if (ret != 1 /*android::keystore::ResponseCode::NO_ERROR*/) {
+ printf("keystore finish error: (%d)\n", /*responses[ret],*/ ret);
+ return disk_decryption_secret_key;
+ } else {
+ //printf("keystore finish operation successful\n");
+ }
+ stop_keystore();
+ return disk_decryption_secret_key;
+}
+
+}}
+
+#define PASSWORD_TOKEN_SIZE 32
+
+bool Free_Return(bool retval, void* weaver_key, void* pwd_salt) {
+ if (weaver_key)
+ free(weaver_key);
+ if (pwd_salt)
+ free(pwd_salt);
+ 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;
+
+ 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.salt);
+ }
+ 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.salt);
+ }
+ //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.salt);
+ }
+ //output_hex(&password_token[0], PASSWORD_TOKEN_SIZE);printf("\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");
+ // Fail over to gatekeeper path for Pixel 1???
+ return Free_Return(retval, weaver_key, pwd.salt);
+ }
+ // 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.salt);
+ }
+ // 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.salt);
+ }
+ // 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.salt);
+ } 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.salt);
+ }
+ //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
+ char application_id[PASSWORD_TOKEN_SIZE + SHA512_DIGEST_LENGTH];
+ 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
+ // 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);
+ if (!secret.size()) {
+ printf("failed to unwrapSyntheticPasswordBlob\n");
+ return Free_Return(retval, weaver_key, pwd.salt);
+ }
+ if (!e4crypt_unlock_user_key(user_id, 0, token.c_str(), secret.c_str())) {
+ printf("e4crypt_unlock_user_key returned fail\n");
+ return Free_Return(retval, weaver_key, pwd.salt);
+ }
+ if (!e4crypt_prepare_user_storage(nullptr, user_id, 0, flags)) {
+ printf("failed to e4crypt_prepare_user_storage\n");
+ return Free_Return(retval, weaver_key, pwd.salt);
+ }
+ printf("Decrypted Successfully!\n");
+ retval = true;
+ return Free_Return(retval, weaver_key, pwd.salt);
+}
+#endif //HAVE_SYNTH_PWD_SUPPORT
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) {
+#ifdef HAVE_SYNTH_PWD_SUPPORT
+ 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
+ return 2; // In TWRP this means pattern
+ else if (pwd.password_type == 2) // In Android this means PIN or password
+ return 1; // In TWRP this means PIN or password
+ return 0; // We'll try the default password
+#else
+ printf("Synthetic password support not present in TWRP\n");
+ return -1;
+#endif
+ }
std::string path;
if (user_id == 0) {
path = "/data/system/";
@@ -123,7 +841,6 @@ int Get_Password_Type(const userid_t user_id, std::string& filename) {
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";
@@ -134,18 +851,6 @@ int Get_Password_Type(const userid_t user_id, std::string& 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;
@@ -167,8 +872,6 @@ bool Decrypt_User(const userid_t user_id, const std::string& Password) {
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");
@@ -181,8 +884,15 @@ bool Decrypt_User(const userid_t user_id, const std::string& Password) {
printf("Decrypted Successfully!\n");
return true;
}
- if (ret!=0)
+ if (stat("/data/system_de/0/spblob", &st) == 0) {
+#ifdef HAVE_SYNTH_PWD_SUPPORT
+ printf("Using synthetic password method\n");
+ return Decrypt_User_Synth_Pass(user_id, Password);
+#else
+ printf("Synthetic password support not present in TWRP\n");
return false;
+#endif
+ }
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));
@@ -194,13 +904,51 @@ bool Decrypt_User(const userid_t user_id, const std::string& Password) {
return false;
}
bool should_reenroll;
- ret = device->verify(device, user_id, 0, (const uint8_t *)handle.c_str(), st.st_size,
+#ifdef HAVE_GATEKEEPER1
+ 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;
+ }
+#else
+ gatekeeper_device_t *gk_device;
+ ret = gatekeeper_device_initialize(&gk_device);
+ if (ret!=0)
+ return false;
+ ret = gk_device->verify(gk_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;
}
+#endif
char token_hex[(auth_token_len*2)+1];
token_hex[(auth_token_len*2)] = 0;
uint32_t i;
diff --git a/crypto/ext4crypt/Ext4Crypt.cpp b/crypto/ext4crypt/Ext4Crypt.cpp
index 8bc419992..ea5b1cf88 100644
--- a/crypto/ext4crypt/Ext4Crypt.cpp
+++ b/crypto/ext4crypt/Ext4Crypt.cpp
@@ -41,8 +41,16 @@
#include <private/android_filesystem_config.h>
+#ifdef HAVE_SYNTH_PWD_SUPPORT
+#include <ext4_utils/ext4_crypt.h>
+#else
#include "ext4_crypt.h"
+#endif
+#ifndef HAVE_LIBKEYUTILS
#include "key_control.h"
+#else
+#include <keyutils.h>
+#endif
#include <hardware/gatekeeper.h>
#include "HashPassword.h"
diff --git a/crypto/ext4crypt/HashPassword.cpp b/crypto/ext4crypt/HashPassword.cpp
index 86e067ebb..817c984bd 100644
--- a/crypto/ext4crypt/HashPassword.cpp
+++ b/crypto/ext4crypt/HashPassword.cpp
@@ -28,16 +28,37 @@
#include <stdlib.h>
#include <openssl/sha.h>
+#include "HashPassword.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();
+void* PersonalizedHashBinary(const char* prefix, const char* key, const size_t key_size) {
+ size_t size = PASS_PADDING_SIZE + key_size;
+ unsigned char* buffer = (unsigned char*)calloc(1, size);
+ if (!buffer) return NULL; // failed to malloc
+ memcpy((void*)buffer, (void*)prefix, strlen(prefix));
+ unsigned char* ptr = buffer + PASS_PADDING_SIZE;
+ memcpy((void*)ptr, key, key_size);
+ unsigned char hash[SHA512_DIGEST_LENGTH];
+ SHA512_CTX sha512;
+ SHA512_Init(&sha512);
+ SHA512_Update(&sha512, buffer, size);
+ SHA512_Final(hash, &sha512);
+ free(buffer);
+ void* ret = malloc(SHA512_DIGEST_LENGTH);
+ if (!ret) return NULL; // failed to malloc
+ memcpy(ret, (void*)&hash[0], SHA512_DIGEST_LENGTH);
+ return ret;
+}
+
+std::string PersonalizedHash(const char* prefix, const char* key, const size_t key_size) {
+ size_t size = PASS_PADDING_SIZE + key_size;
unsigned char* buffer = (unsigned char*)calloc(1, size);
- const char* prefix = "Android FBE credential hash";
+ if (!buffer) return ""; // failed to malloc
memcpy((void*)buffer, (void*)prefix, strlen(prefix));
unsigned char* ptr = buffer + PASS_PADDING_SIZE;
- memcpy((void*)ptr, Password.c_str(), Password.size());
+ memcpy((void*)ptr, key, key_size);
unsigned char hash[SHA512_DIGEST_LENGTH];
SHA512_CTX sha512;
SHA512_Init(&sha512);
@@ -49,5 +70,15 @@ std::string HashPassword(const std::string& Password) {
sprintf(hex_hash + (index * 2), "%02X", hash[index]);
hex_hash[128] = 0;
std::string ret = hex_hash;
+ free(buffer);
return ret;
}
+
+std::string PersonalizedHash(const char* prefix, const std::string& Password) {
+ return PersonalizedHash(prefix, Password.c_str(), Password.size());
+}
+
+std::string HashPassword(const std::string& Password) {
+ const char* prefix = FBE_PERSONALIZATION;
+ return PersonalizedHash(prefix, Password);
+}
diff --git a/crypto/ext4crypt/HashPassword.h b/crypto/ext4crypt/HashPassword.h
index d9b5ce5f1..8abd0de71 100644
--- a/crypto/ext4crypt/HashPassword.h
+++ b/crypto/ext4crypt/HashPassword.h
@@ -19,6 +19,16 @@
#include <string>
+#define FBE_PERSONALIZATION "Android FBE credential hash"
+#define PERSONALISATION_WEAVER_KEY "weaver-key"
+#define PERSONALISATION_WEAVER_PASSWORD "weaver-pwd"
+#define PERSONALISATION_APPLICATION_ID "application-id"
+#define PERSONALIZATION_FBE_KEY "fbe-key"
+
+void* PersonalizedHashBinary(const char* prefix, const char* key, const size_t key_size);
+
+std::string PersonalizedHash(const char* prefix, const char* key, const size_t key_size);
+std::string PersonalizedHash(const char* prefix, const std::string& Password);
std::string HashPassword(const std::string& Password);
#endif
diff --git a/crypto/ext4crypt/KeyStorage3.cpp b/crypto/ext4crypt/KeyStorage3.cpp
new file mode 100644
index 000000000..a07212d9d
--- /dev/null
+++ b/crypto/ext4crypt/KeyStorage3.cpp
@@ -0,0 +1,526 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "KeyStorage3.h"
+
+#include "Keymaster3.h"
+#include "ScryptParameters.h"
+#include "Utils.h"
+
+#include <vector>
+
+#include <errno.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/sha.h>
+
+#include <android-base/file.h>
+//#include <android-base/logging.h>
+
+#include <cutils/properties.h>
+
+#include <hardware/hw_auth_token.h>
+
+#include <keystore/authorization_set.h>
+#include <keystore/keystore_hidl_support.h>
+
+extern "C" {
+
+#include "crypto_scrypt.h"
+}
+
+#include <iostream>
+#define ERROR 1
+#define LOG(x) std::cout
+#define PLOG(x) std::cout
+
+namespace android {
+namespace vold {
+using namespace keystore;
+
+const KeyAuthentication kEmptyAuthentication{"", ""};
+
+static constexpr size_t AES_KEY_BYTES = 32;
+static constexpr size_t GCM_NONCE_BYTES = 12;
+static constexpr size_t GCM_MAC_BYTES = 16;
+static constexpr size_t SALT_BYTES = 1 << 4;
+static constexpr size_t SECDISCARDABLE_BYTES = 1 << 14;
+static constexpr size_t STRETCHED_BYTES = 1 << 6;
+
+static constexpr uint32_t AUTH_TIMEOUT = 30; // Seconds
+
+static const char* kCurrentVersion = "1";
+static const char* kRmPath = "/system/bin/rm";
+static const char* kSecdiscardPath = "/system/bin/secdiscard";
+static const char* kStretch_none = "none";
+static const char* kStretch_nopassword = "nopassword";
+static const std::string kStretchPrefix_scrypt = "scrypt ";
+static const char* kHashPrefix_secdiscardable = "Android secdiscardable SHA512";
+static const char* kHashPrefix_keygen = "Android key wrapping key generation SHA512";
+static const char* kFn_encrypted_key = "encrypted_key";
+static const char* kFn_keymaster_key_blob = "keymaster_key_blob";
+static const char* kFn_keymaster_key_blob_upgraded = "keymaster_key_blob_upgraded";
+static const char* kFn_salt = "salt";
+static const char* kFn_secdiscardable = "secdiscardable";
+static const char* kFn_stretching = "stretching";
+static const char* kFn_version = "version";
+
+static bool checkSize(const std::string& kind, size_t actual, size_t expected) {
+ if (actual != expected) {
+ LOG(ERROR) << "Wrong number of bytes in " << kind << ", expected " << expected << " got "
+ << actual;
+ return false;
+ }
+ return true;
+}
+
+static std::string hashWithPrefix(char const* prefix, const std::string& tohash) {
+ SHA512_CTX c;
+
+ SHA512_Init(&c);
+ // Personalise the hashing by introducing a fixed prefix.
+ // Hashing applications should use personalization except when there is a
+ // specific reason not to; see section 4.11 of https://www.schneier.com/skein1.3.pdf
+ std::string hashingPrefix = prefix;
+ hashingPrefix.resize(SHA512_CBLOCK);
+ SHA512_Update(&c, hashingPrefix.data(), hashingPrefix.size());
+ SHA512_Update(&c, tohash.data(), tohash.size());
+ std::string res(SHA512_DIGEST_LENGTH, '\0');
+ SHA512_Final(reinterpret_cast<uint8_t*>(&res[0]), &c);
+ return res;
+}
+
+/*static bool generateKeymasterKey(Keymaster& keymaster, const KeyAuthentication& auth,
+ const std::string& appId, std::string* key) {
+ auto paramBuilder = AuthorizationSetBuilder()
+ .AesEncryptionKey(AES_KEY_BYTES * 8)
+ .Authorization(TAG_BLOCK_MODE, BlockMode::GCM)
+ .Authorization(TAG_MIN_MAC_LENGTH, GCM_MAC_BYTES * 8)
+ .Authorization(TAG_PADDING, PaddingMode::NONE)
+ .Authorization(TAG_APPLICATION_ID, blob2hidlVec(appId));
+ if (auth.token.empty()) {
+ LOG(DEBUG) << "Creating key that doesn't need auth token";
+ paramBuilder.Authorization(TAG_NO_AUTH_REQUIRED);
+ } else {
+ LOG(DEBUG) << "Auth token required for key";
+ if (auth.token.size() != sizeof(hw_auth_token_t)) {
+ LOG(ERROR) << "Auth token should be " << sizeof(hw_auth_token_t) << " bytes, was "
+ << auth.token.size() << " bytes";
+ return false;
+ }
+ const hw_auth_token_t* at = reinterpret_cast<const hw_auth_token_t*>(auth.token.data());
+ paramBuilder.Authorization(TAG_USER_SECURE_ID, at->user_id);
+ paramBuilder.Authorization(TAG_USER_AUTH_TYPE, HardwareAuthenticatorType::PASSWORD);
+ paramBuilder.Authorization(TAG_AUTH_TIMEOUT, AUTH_TIMEOUT);
+ }
+ return keymaster.generateKey(paramBuilder, key);
+}*/
+
+static AuthorizationSet beginParams(const KeyAuthentication& auth,
+ const std::string& appId) {
+ auto paramBuilder = AuthorizationSetBuilder()
+ .Authorization(TAG_BLOCK_MODE, BlockMode::GCM)
+ .Authorization(TAG_MAC_LENGTH, GCM_MAC_BYTES * 8)
+ .Authorization(TAG_PADDING, PaddingMode::NONE)
+ .Authorization(TAG_APPLICATION_ID, blob2hidlVec(appId));
+ if (!auth.token.empty()) {
+ LOG(DEBUG) << "Supplying auth token to Keymaster";
+ paramBuilder.Authorization(TAG_AUTH_TOKEN, blob2hidlVec(auth.token));
+ }
+ return paramBuilder;
+}
+
+static bool readFileToString(const std::string& filename, std::string* result) {
+ if (!android::base::ReadFileToString(filename, result)) {
+ PLOG(ERROR) << "Failed to read from " << filename;
+ return false;
+ }
+ return true;
+}
+
+static bool writeStringToFile(const std::string& payload, const std::string& filename) {
+ if (!android::base::WriteStringToFile(payload, filename)) {
+ PLOG(ERROR) << "Failed to write to " << filename;
+ return false;
+ }
+ return true;
+}
+
+static KeymasterOperation begin(Keymaster& keymaster, const std::string& dir,
+ KeyPurpose purpose,
+ const AuthorizationSet &keyParams,
+ const AuthorizationSet &opParams,
+ AuthorizationSet* outParams) {
+ auto kmKeyPath = dir + "/" + kFn_keymaster_key_blob;
+ std::string kmKey;
+ if (!readFileToString(kmKeyPath, &kmKey)) return KeymasterOperation();
+ AuthorizationSet inParams(keyParams);
+ inParams.append(opParams.begin(), opParams.end());
+ for (;;) {
+ auto opHandle = keymaster.begin(purpose, kmKey, inParams, outParams);
+ if (opHandle) {
+ return opHandle;
+ }
+ if (opHandle.errorCode() != ErrorCode::KEY_REQUIRES_UPGRADE) return opHandle;
+ LOG(DEBUG) << "Upgrading key: " << dir;
+ std::string newKey;
+ if (!keymaster.upgradeKey(kmKey, keyParams, &newKey)) return KeymasterOperation();
+ // Upgrade the key in memory but do not replace the key in storage
+ /*auto newKeyPath = dir + "/" + kFn_keymaster_key_blob_upgraded;
+ if (!writeStringToFile(newKey, newKeyPath)) return KeymasterOperation();
+ if (rename(newKeyPath.c_str(), kmKeyPath.c_str()) != 0) {
+ PLOG(ERROR) << "Unable to move upgraded key to location: " << kmKeyPath;
+ return KeymasterOperation();
+ }
+ if (!keymaster.deleteKey(kmKey)) {
+ LOG(ERROR) << "Key deletion failed during upgrade, continuing anyway: " << dir;
+ }*/
+ kmKey = newKey;
+ LOG(INFO) << "Key upgraded: " << dir;
+ }
+}
+
+/*static bool encryptWithKeymasterKey(Keymaster& keymaster, const std::string& dir,
+ const AuthorizationSet &keyParams,
+ const std::string& message, std::string* ciphertext) {
+ AuthorizationSet opParams;
+ AuthorizationSet outParams;
+ auto opHandle = begin(keymaster, dir, KeyPurpose::ENCRYPT, keyParams, opParams, &outParams);
+ if (!opHandle) return false;
+ auto nonceBlob = outParams.GetTagValue(TAG_NONCE);
+ if (!nonceBlob.isOk()) {
+ LOG(ERROR) << "GCM encryption but no nonce generated";
+ return false;
+ }
+ // nonceBlob here is just a pointer into existing data, must not be freed
+ std::string nonce(reinterpret_cast<const char*>(&nonceBlob.value()[0]), nonceBlob.value().size());
+ if (!checkSize("nonce", nonce.size(), GCM_NONCE_BYTES)) return false;
+ std::string body;
+ if (!opHandle.updateCompletely(message, &body)) return false;
+
+ std::string mac;
+ if (!opHandle.finish(&mac)) return false;
+ if (!checkSize("mac", mac.size(), GCM_MAC_BYTES)) return false;
+ *ciphertext = nonce + body + mac;
+ return true;
+}*/
+
+static bool decryptWithKeymasterKey(Keymaster& keymaster, const std::string& dir,
+ const AuthorizationSet &keyParams,
+ const std::string& ciphertext, std::string* message) {
+ auto nonce = ciphertext.substr(0, GCM_NONCE_BYTES);
+ auto bodyAndMac = ciphertext.substr(GCM_NONCE_BYTES);
+ auto opParams = AuthorizationSetBuilder()
+ .Authorization(TAG_NONCE, blob2hidlVec(nonce));
+ auto opHandle = begin(keymaster, dir, KeyPurpose::DECRYPT, keyParams, opParams, nullptr);
+ if (!opHandle) return false;
+ if (!opHandle.updateCompletely(bodyAndMac, message)) return false;
+ if (!opHandle.finish(nullptr)) return false;
+ return true;
+}
+
+static std::string getStretching(const KeyAuthentication& auth) {
+ if (!auth.usesKeymaster()) {
+ return kStretch_none;
+ } else if (auth.secret.empty()) {
+ return kStretch_nopassword;
+ } else {
+ char paramstr[PROPERTY_VALUE_MAX];
+
+ property_get(SCRYPT_PROP, paramstr, SCRYPT_DEFAULTS);
+ return std::string() + kStretchPrefix_scrypt + paramstr;
+ }
+}
+
+static bool stretchingNeedsSalt(const std::string& stretching) {
+ return stretching != kStretch_nopassword && stretching != kStretch_none;
+}
+
+static bool stretchSecret(const std::string& stretching, const std::string& secret,
+ const std::string& salt, std::string* stretched) {
+ if (stretching == kStretch_nopassword) {
+ if (!secret.empty()) {
+ LOG(WARNING) << "Password present but stretching is nopassword";
+ // Continue anyway
+ }
+ stretched->clear();
+ } else if (stretching == kStretch_none) {
+ *stretched = secret;
+ } else if (std::equal(kStretchPrefix_scrypt.begin(), kStretchPrefix_scrypt.end(),
+ stretching.begin())) {
+ int Nf, rf, pf;
+ if (!parse_scrypt_parameters(stretching.substr(kStretchPrefix_scrypt.size()).c_str(), &Nf,
+ &rf, &pf)) {
+ LOG(ERROR) << "Unable to parse scrypt params in stretching: " << stretching;
+ return false;
+ }
+ stretched->assign(STRETCHED_BYTES, '\0');
+ if (crypto_scrypt(reinterpret_cast<const uint8_t*>(secret.data()), secret.size(),
+ reinterpret_cast<const uint8_t*>(salt.data()), salt.size(),
+ 1 << Nf, 1 << rf, 1 << pf,
+ reinterpret_cast<uint8_t*>(&(*stretched)[0]), stretched->size()) != 0) {
+ LOG(ERROR) << "scrypt failed with params: " << stretching;
+ return false;
+ }
+ } else {
+ LOG(ERROR) << "Unknown stretching type: " << stretching;
+ return false;
+ }
+ return true;
+}
+
+static bool generateAppId(const KeyAuthentication& auth, const std::string& stretching,
+ const std::string& salt, const std::string& secdiscardable,
+ std::string* appId) {
+ std::string stretched;
+ if (!stretchSecret(stretching, auth.secret, salt, &stretched)) return false;
+ *appId = hashWithPrefix(kHashPrefix_secdiscardable, secdiscardable) + stretched;
+ return true;
+}
+
+static bool readRandomBytesOrLog(size_t count, std::string* out) {
+ auto status = ReadRandomBytes(count, *out);
+ if (status != OK) {
+ LOG(ERROR) << "Random read failed with status: " << status;
+ return false;
+ }
+ return true;
+}
+
+static void logOpensslError() {
+ LOG(ERROR) << "Openssl error: " << ERR_get_error();
+}
+
+static bool encryptWithoutKeymaster(const std::string& preKey,
+ const std::string& plaintext, std::string* ciphertext) {
+ auto key = hashWithPrefix(kHashPrefix_keygen, preKey);
+ key.resize(AES_KEY_BYTES);
+ if (!readRandomBytesOrLog(GCM_NONCE_BYTES, ciphertext)) return false;
+ auto ctx = std::unique_ptr<EVP_CIPHER_CTX, decltype(&::EVP_CIPHER_CTX_free)>(
+ EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free);
+ if (!ctx) {
+ logOpensslError();
+ return false;
+ }
+ if (1 != EVP_EncryptInit_ex(ctx.get(), EVP_aes_256_gcm(), NULL,
+ reinterpret_cast<const uint8_t*>(key.data()),
+ reinterpret_cast<const uint8_t*>(ciphertext->data()))) {
+ logOpensslError();
+ return false;
+ }
+ ciphertext->resize(GCM_NONCE_BYTES + plaintext.size() + GCM_MAC_BYTES);
+ int outlen;
+ if (1 != EVP_EncryptUpdate(ctx.get(),
+ reinterpret_cast<uint8_t*>(&(*ciphertext)[0] + GCM_NONCE_BYTES), &outlen,
+ reinterpret_cast<const uint8_t*>(plaintext.data()), plaintext.size())) {
+ logOpensslError();
+ return false;
+ }
+ if (outlen != static_cast<int>(plaintext.size())) {
+ LOG(ERROR) << "GCM ciphertext length should be " << plaintext.size() << " was " << outlen;
+ return false;
+ }
+ if (1 != EVP_EncryptFinal_ex(ctx.get(),
+ reinterpret_cast<uint8_t*>(&(*ciphertext)[0] + GCM_NONCE_BYTES + plaintext.size()), &outlen)) {
+ logOpensslError();
+ return false;
+ }
+ if (outlen != 0) {
+ LOG(ERROR) << "GCM EncryptFinal should be 0, was " << outlen;
+ return false;
+ }
+ if (1 != EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_GET_TAG, GCM_MAC_BYTES,
+ reinterpret_cast<uint8_t*>(&(*ciphertext)[0] + GCM_NONCE_BYTES + plaintext.size()))) {
+ logOpensslError();
+ return false;
+ }
+ return true;
+}
+
+static bool decryptWithoutKeymaster(const std::string& preKey,
+ const std::string& ciphertext, std::string* plaintext) {
+ if (ciphertext.size() < GCM_NONCE_BYTES + GCM_MAC_BYTES) {
+ LOG(ERROR) << "GCM ciphertext too small: " << ciphertext.size();
+ return false;
+ }
+ auto key = hashWithPrefix(kHashPrefix_keygen, preKey);
+ key.resize(AES_KEY_BYTES);
+ auto ctx = std::unique_ptr<EVP_CIPHER_CTX, decltype(&::EVP_CIPHER_CTX_free)>(
+ EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free);
+ if (!ctx) {
+ logOpensslError();
+ return false;
+ }
+ if (1 != EVP_DecryptInit_ex(ctx.get(), EVP_aes_256_gcm(), NULL,
+ reinterpret_cast<const uint8_t*>(key.data()),
+ reinterpret_cast<const uint8_t*>(ciphertext.data()))) {
+ logOpensslError();
+ return false;
+ }
+ plaintext->resize(ciphertext.size() - GCM_NONCE_BYTES - GCM_MAC_BYTES);
+ int outlen;
+ if (1 != EVP_DecryptUpdate(ctx.get(),
+ reinterpret_cast<uint8_t*>(&(*plaintext)[0]), &outlen,
+ reinterpret_cast<const uint8_t*>(ciphertext.data() + GCM_NONCE_BYTES), plaintext->size())) {
+ logOpensslError();
+ return false;
+ }
+ if (outlen != static_cast<int>(plaintext->size())) {
+ LOG(ERROR) << "GCM plaintext length should be " << plaintext->size() << " was " << outlen;
+ return false;
+ }
+ if (1 != EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_SET_TAG, GCM_MAC_BYTES,
+ const_cast<void *>(
+ reinterpret_cast<const void*>(ciphertext.data() + GCM_NONCE_BYTES + plaintext->size())))) {
+ logOpensslError();
+ return false;
+ }
+ if (1 != EVP_DecryptFinal_ex(ctx.get(),
+ reinterpret_cast<uint8_t*>(&(*plaintext)[0] + plaintext->size()), &outlen)) {
+ logOpensslError();
+ return false;
+ }
+ if (outlen != 0) {
+ LOG(ERROR) << "GCM EncryptFinal should be 0, was " << outlen;
+ return false;
+ }
+ return true;
+}
+
+/*bool storeKey(const std::string& dir, const KeyAuthentication& auth, const std::string& key) {
+ if (TEMP_FAILURE_RETRY(mkdir(dir.c_str(), 0700)) == -1) {
+ PLOG(ERROR) << "key mkdir " << dir;
+ return false;
+ }
+ if (!writeStringToFile(kCurrentVersion, dir + "/" + kFn_version)) return false;
+ std::string secdiscardable;
+ if (!readRandomBytesOrLog(SECDISCARDABLE_BYTES, &secdiscardable)) return false;
+ if (!writeStringToFile(secdiscardable, dir + "/" + kFn_secdiscardable)) return false;
+ std::string stretching = getStretching(auth);
+ if (!writeStringToFile(stretching, dir + "/" + kFn_stretching)) return false;
+ std::string salt;
+ if (stretchingNeedsSalt(stretching)) {
+ if (ReadRandomBytes(SALT_BYTES, salt) != OK) {
+ LOG(ERROR) << "Random read failed";
+ return false;
+ }
+ if (!writeStringToFile(salt, dir + "/" + kFn_salt)) return false;
+ }
+ std::string appId;
+ if (!generateAppId(auth, stretching, salt, secdiscardable, &appId)) return false;
+ std::string encryptedKey;
+ if (auth.usesKeymaster()) {
+ Keymaster keymaster;
+ if (!keymaster) return false;
+ std::string kmKey;
+ if (!generateKeymasterKey(keymaster, auth, appId, &kmKey)) return false;
+ if (!writeStringToFile(kmKey, dir + "/" + kFn_keymaster_key_blob)) return false;
+ auto keyParams = beginParams(auth, appId);
+ if (!encryptWithKeymasterKey(keymaster, dir, keyParams, key, &encryptedKey)) return false;
+ } else {
+ if (!encryptWithoutKeymaster(appId, key, &encryptedKey)) return false;
+ }
+ if (!writeStringToFile(encryptedKey, dir + "/" + kFn_encrypted_key)) return false;
+ return true;
+}*/
+
+bool retrieveKey(const std::string& dir, const KeyAuthentication& auth, std::string* key) {
+ std::string version;
+ if (!readFileToString(dir + "/" + kFn_version, &version)) return false;
+ if (version != kCurrentVersion) {
+ LOG(ERROR) << "Version mismatch, expected " << kCurrentVersion << " got " << version;
+ return false;
+ }
+ std::string secdiscardable;
+ if (!readFileToString(dir + "/" + kFn_secdiscardable, &secdiscardable)) return false;
+ std::string stretching;
+ if (!readFileToString(dir + "/" + kFn_stretching, &stretching)) return false;
+ std::string salt;
+ if (stretchingNeedsSalt(stretching)) {
+ if (!readFileToString(dir + "/" + kFn_salt, &salt)) return false;
+ }
+ std::string appId;
+ if (!generateAppId(auth, stretching, salt, secdiscardable, &appId)) return false;
+ std::string encryptedMessage;
+ if (!readFileToString(dir + "/" + kFn_encrypted_key, &encryptedMessage)) return false;
+ if (auth.usesKeymaster()) {
+ Keymaster keymaster;
+ if (!keymaster) return false;
+ auto keyParams = beginParams(auth, appId);
+ if (!decryptWithKeymasterKey(keymaster, dir, keyParams, encryptedMessage, key)) return false;
+ } else {
+ if (!decryptWithoutKeymaster(appId, encryptedMessage, key)) return false;
+ }
+ return true;
+}
+
+static bool deleteKey(const std::string& dir) {
+ std::string kmKey;
+ if (!readFileToString(dir + "/" + kFn_keymaster_key_blob, &kmKey)) return false;
+ Keymaster keymaster;
+ if (!keymaster) return false;
+ if (!keymaster.deleteKey(kmKey)) return false;
+ return true;
+}
+
+static bool runSecdiscard(const std::string& dir) {
+ if (ForkExecvp(
+ std::vector<std::string>{kSecdiscardPath, "--",
+ dir + "/" + kFn_encrypted_key,
+ dir + "/" + kFn_keymaster_key_blob,
+ dir + "/" + kFn_secdiscardable,
+ }) != 0) {
+ LOG(ERROR) << "secdiscard failed";
+ return false;
+ }
+ return true;
+}
+
+bool runSecdiscardSingle(const std::string& file) {
+ if (ForkExecvp(
+ std::vector<std::string>{kSecdiscardPath, "--",
+ file}) != 0) {
+ LOG(ERROR) << "secdiscard failed";
+ return false;
+ }
+ return true;
+}
+
+static bool recursiveDeleteKey(const std::string& dir) {
+ if (ForkExecvp(std::vector<std::string>{kRmPath, "-rf", dir}) != 0) {
+ LOG(ERROR) << "recursive delete failed";
+ return false;
+ }
+ return true;
+}
+
+bool destroyKey(const std::string& dir) {
+ bool success = true;
+ // Try each thing, even if previous things failed.
+ success &= deleteKey(dir);
+ success &= runSecdiscard(dir);
+ success &= recursiveDeleteKey(dir);
+ return success;
+}
+
+} // namespace vold
+} // namespace android
diff --git a/crypto/ext4crypt/KeyStorage3.h b/crypto/ext4crypt/KeyStorage3.h
new file mode 100644
index 000000000..bce6a99c5
--- /dev/null
+++ b/crypto/ext4crypt/KeyStorage3.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_VOLD_KEYSTORAGE_H
+#define ANDROID_VOLD_KEYSTORAGE_H
+
+#include <string>
+
+namespace android {
+namespace vold {
+
+// Represents the information needed to decrypt a disk encryption key.
+// If "token" is nonempty, it is passed in as a required Gatekeeper auth token.
+// If "token" and "secret" are nonempty, "secret" is appended to the application-specific
+// binary needed to unlock.
+// If only "secret" is nonempty, it is used to decrypt in a non-Keymaster process.
+class KeyAuthentication {
+ public:
+ KeyAuthentication(std::string t, std::string s) : token{t}, secret{s} {};
+
+ bool usesKeymaster() const { return !token.empty() || secret.empty(); };
+
+ const std::string token;
+ const std::string secret;
+};
+
+extern const KeyAuthentication kEmptyAuthentication;
+
+// Create a directory at the named path, and store "key" in it,
+// in such a way that it can only be retrieved via Keymaster and
+// can be securely deleted.
+// It's safe to move/rename the directory after creation.
+bool storeKey(const std::string& dir, const KeyAuthentication& auth, const std::string& key);
+
+// Retrieve the key from the named directory.
+bool retrieveKey(const std::string& dir, const KeyAuthentication& auth, std::string* key);
+
+// Securely destroy the key stored in the named directory and delete the directory.
+bool destroyKey(const std::string& dir);
+
+bool runSecdiscardSingle(const std::string& file);
+} // namespace vold
+} // namespace android
+
+#endif
diff --git a/crypto/ext4crypt/Keymaster3.cpp b/crypto/ext4crypt/Keymaster3.cpp
new file mode 100644
index 000000000..c72ddd0c3
--- /dev/null
+++ b/crypto/ext4crypt/Keymaster3.cpp
@@ -0,0 +1,324 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Keymaster3.h"
+
+//#include <android-base/logging.h>
+#include <keystore/keymaster_tags.h>
+#include <keystore/authorization_set.h>
+#include <keystore/keystore_hidl_support.h>
+
+#include <iostream>
+#define ERROR 1
+#define LOG(x) std::cout
+
+using namespace ::keystore;
+using android::hardware::hidl_string;
+
+namespace android {
+namespace vold {
+
+KeymasterOperation::~KeymasterOperation() {
+ if (mDevice.get()) mDevice->abort(mOpHandle);
+}
+
+bool KeymasterOperation::updateCompletely(const std::string& input, std::string* output) {
+ if (output)
+ output->clear();
+ auto it = input.begin();
+ uint32_t inputConsumed;
+
+ ErrorCode km_error;
+ auto hidlCB = [&] (ErrorCode ret, uint32_t _inputConsumed,
+ const hidl_vec<KeyParameter>& /*ignored*/, const hidl_vec<uint8_t>& _output) {
+ km_error = ret;
+ if (km_error != ErrorCode::OK) return;
+ inputConsumed = _inputConsumed;
+ if (output)
+ output->append(reinterpret_cast<const char*>(&_output[0]), _output.size());
+ };
+
+ while (it != input.end()) {
+ size_t toRead = static_cast<size_t>(input.end() - it);
+ auto inputBlob = blob2hidlVec(reinterpret_cast<const uint8_t*>(&*it), toRead);
+ auto error = mDevice->update(mOpHandle, hidl_vec<KeyParameter>(), inputBlob, hidlCB);
+ if (!error.isOk()) {
+ LOG(ERROR) << "update failed: " << error.description();
+ mDevice = nullptr;
+ return false;
+ }
+ if (km_error != ErrorCode::OK) {
+ LOG(ERROR) << "update failed, code " << int32_t(km_error);
+ mDevice = nullptr;
+ return false;
+ }
+ if (inputConsumed > toRead) {
+ LOG(ERROR) << "update reported too much input consumed";
+ mDevice = nullptr;
+ return false;
+ }
+ it += inputConsumed;
+ }
+ return true;
+}
+
+bool KeymasterOperation::finish(std::string* output) {
+ ErrorCode km_error;
+ auto hidlCb = [&] (ErrorCode ret, const hidl_vec<KeyParameter>& /*ignored*/,
+ const hidl_vec<uint8_t>& _output) {
+ km_error = ret;
+ if (km_error != ErrorCode::OK) return;
+ if (output)
+ output->assign(reinterpret_cast<const char*>(&_output[0]), _output.size());
+ };
+ auto error = mDevice->finish(mOpHandle, hidl_vec<KeyParameter>(), hidl_vec<uint8_t>(),
+ hidl_vec<uint8_t>(), hidlCb);
+ mDevice = nullptr;
+ if (!error.isOk()) {
+ LOG(ERROR) << "finish failed: " << error.description();
+ return false;
+ }
+ if (km_error != ErrorCode::OK) {
+ LOG(ERROR) << "finish failed, code " << int32_t(km_error);
+ return false;
+ }
+ return true;
+}
+
+Keymaster::Keymaster() {
+ mDevice = ::android::hardware::keymaster::V3_0::IKeymasterDevice::getService();
+}
+
+/*bool Keymaster::generateKey(const AuthorizationSet& inParams, std::string* key) {
+ ErrorCode km_error;
+ auto hidlCb = [&] (ErrorCode ret, const hidl_vec<uint8_t>& keyBlob,
+ const KeyCharacteristics& /*ignored* /) {
+ km_error = ret;
+ if (km_error != ErrorCode::OK) return;
+ if (key)
+ key->assign(reinterpret_cast<const char*>(&keyBlob[0]), keyBlob.size());
+ };
+
+ auto error = mDevice->generateKey(inParams.hidl_data(), hidlCb);
+ if (!error.isOk()) {
+ LOG(ERROR) << "generate_key failed: " << error.description();
+ return false;
+ }
+ if (km_error != ErrorCode::OK) {
+ LOG(ERROR) << "generate_key failed, code " << int32_t(km_error);
+ return false;
+ }
+ return true;
+}*/
+
+bool Keymaster::deleteKey(const std::string& key) {
+ LOG(ERROR) << "NOT deleting key in TWRP";
+ return false;
+ /*auto keyBlob = blob2hidlVec(key);
+ auto error = mDevice->deleteKey(keyBlob);
+ if (!error.isOk()) {
+ LOG(ERROR) << "delete_key failed: " << error.description();
+ return false;
+ }
+ if (ErrorCode(error) != ErrorCode::OK) {
+ LOG(ERROR) << "delete_key failed, code " << uint32_t(ErrorCode(error));
+ return false;
+ }
+ return true;*/
+}
+
+bool Keymaster::upgradeKey(const std::string& oldKey, const AuthorizationSet& inParams,
+ std::string* newKey) {
+ auto oldKeyBlob = blob2hidlVec(oldKey);
+ ErrorCode km_error;
+ auto hidlCb = [&] (ErrorCode ret, const hidl_vec<uint8_t>& upgradedKeyBlob) {
+ km_error = ret;
+ if (km_error != ErrorCode::OK) return;
+ if (newKey)
+ newKey->assign(reinterpret_cast<const char*>(&upgradedKeyBlob[0]),
+ upgradedKeyBlob.size());
+ };
+ auto error = mDevice->upgradeKey(oldKeyBlob, inParams.hidl_data(), hidlCb);
+ if (!error.isOk()) {
+ LOG(ERROR) << "upgrade_key failed: " << error.description();
+ return false;
+ }
+ if (km_error != ErrorCode::OK) {
+ LOG(ERROR) << "upgrade_key failed, code " << int32_t(km_error);
+ return false;
+ }
+ return true;
+}
+
+KeymasterOperation Keymaster::begin(KeyPurpose purpose, const std::string& key,
+ const AuthorizationSet& inParams,
+ AuthorizationSet* outParams) {
+ auto keyBlob = blob2hidlVec(key);
+ uint64_t mOpHandle;
+ ErrorCode km_error;
+
+ auto hidlCb = [&] (ErrorCode ret, const hidl_vec<KeyParameter>& _outParams,
+ uint64_t operationHandle) {
+ km_error = ret;
+ if (km_error != ErrorCode::OK) return;
+ if (outParams)
+ *outParams = _outParams;
+ mOpHandle = operationHandle;
+ };
+
+ auto error = mDevice->begin(purpose, keyBlob, inParams.hidl_data(), hidlCb);
+ if (!error.isOk()) {
+ LOG(ERROR) << "begin failed: " << error.description();
+ return KeymasterOperation(ErrorCode::UNKNOWN_ERROR);
+ }
+ if (km_error != ErrorCode::OK) {
+ LOG(ERROR) << "begin failed, code " << int32_t(km_error);
+ return KeymasterOperation(km_error);
+ }
+ return KeymasterOperation(mDevice, mOpHandle);
+}
+bool Keymaster::isSecure() {
+ bool _isSecure = false;
+ auto rc = mDevice->getHardwareFeatures(
+ [&] (bool isSecure, bool, bool, bool, bool, const hidl_string&, const hidl_string&) {
+ _isSecure = isSecure; });
+ return rc.isOk() && _isSecure;
+}
+
+} // namespace vold
+} // namespace android
+
+using namespace ::android::vold;
+
+int keymaster_compatibility_cryptfs_scrypt() {
+ Keymaster dev;
+ if (!dev) {
+ LOG(ERROR) << "Failed to initiate keymaster session";
+ return -1;
+ }
+ return dev.isSecure();
+}
+
+/*int keymaster_create_key_for_cryptfs_scrypt(uint32_t rsa_key_size,
+ uint64_t rsa_exponent,
+ uint32_t ratelimit,
+ uint8_t* key_buffer,
+ uint32_t key_buffer_size,
+ uint32_t* key_out_size)
+{
+ Keymaster dev;
+ std::string key;
+ if (!dev) {
+ LOG(ERROR) << "Failed to initiate keymaster session";
+ return -1;
+ }
+ if (!key_buffer || !key_out_size) {
+ LOG(ERROR) << __FILE__ << ":" << __LINE__ << ":Invalid argument";
+ return -1;
+ }
+ if (key_out_size) {
+ *key_out_size = 0;
+ }
+
+ auto paramBuilder = AuthorizationSetBuilder()
+ .Authorization(TAG_ALGORITHM, Algorithm::RSA)
+ .Authorization(TAG_KEY_SIZE, rsa_key_size)
+ .Authorization(TAG_RSA_PUBLIC_EXPONENT, rsa_exponent)
+ .Authorization(TAG_PURPOSE, KeyPurpose::SIGN)
+ .Authorization(TAG_PADDING, PaddingMode::NONE)
+ .Authorization(TAG_DIGEST, Digest::NONE)
+ .Authorization(TAG_BLOB_USAGE_REQUIREMENTS,
+ KeyBlobUsageRequirements::STANDALONE)
+ .Authorization(TAG_NO_AUTH_REQUIRED)
+ .Authorization(TAG_MIN_SECONDS_BETWEEN_OPS, ratelimit);
+
+ if (!dev.generateKey(paramBuilder, &key)) {
+ return -1;
+ }
+
+ if (key_out_size) {
+ *key_out_size = key.size();
+ }
+
+ if (key_buffer_size < key.size()) {
+ return -1;
+ }
+
+ std::copy(key.data(), key.data() + key.size(), key_buffer);
+ return 0;
+}
+
+int keymaster_sign_object_for_cryptfs_scrypt(const uint8_t* key_blob,
+ size_t key_blob_size,
+ uint32_t ratelimit,
+ const uint8_t* object,
+ const size_t object_size,
+ uint8_t** signature_buffer,
+ size_t* signature_buffer_size)
+{
+ Keymaster dev;
+ if (!dev) {
+ LOG(ERROR) << "Failed to initiate keymaster session";
+ return -1;
+ }
+ if (!key_blob || !object || !signature_buffer || !signature_buffer_size) {
+ LOG(ERROR) << __FILE__ << ":" << __LINE__ << ":Invalid argument";
+ return -1;
+ }
+
+ AuthorizationSet outParams;
+ std::string key(reinterpret_cast<const char*>(key_blob), key_blob_size);
+ std::string input(reinterpret_cast<const char*>(object), object_size);
+ std::string output;
+ KeymasterOperation op;
+
+ auto paramBuilder = AuthorizationSetBuilder()
+ .Authorization(TAG_PADDING, PaddingMode::NONE)
+ .Authorization(TAG_DIGEST, Digest::NONE);
+
+ while (true) {
+ op = dev.begin(KeyPurpose::SIGN, key, paramBuilder, &outParams);
+ if (op.errorCode() == ErrorCode::KEY_RATE_LIMIT_EXCEEDED) {
+ sleep(ratelimit);
+ continue;
+ } else break;
+ }
+
+ if (op.errorCode() != ErrorCode::OK) {
+ LOG(ERROR) << "Error starting keymaster signature transaction: " << int32_t(op.errorCode());
+ return -1;
+ }
+
+ if (!op.updateCompletely(input, &output)) {
+ LOG(ERROR) << "Error sending data to keymaster signature transaction: "
+ << uint32_t(op.errorCode());
+ return -1;
+ }
+
+ if (!op.finish(&output)) {
+ LOG(ERROR) << "Error finalizing keymaster signature transaction: " << int32_t(op.errorCode());
+ return -1;
+ }
+
+ *signature_buffer = reinterpret_cast<uint8_t*>(malloc(output.size()));
+ if (*signature_buffer == nullptr) {
+ LOG(ERROR) << "Error allocation buffer for keymaster signature";
+ return -1;
+ }
+ *signature_buffer_size = output.size();
+ std::copy(output.data(), output.data() + output.size(), *signature_buffer);
+ return 0;
+}*/
diff --git a/crypto/ext4crypt/Keymaster3.h b/crypto/ext4crypt/Keymaster3.h
new file mode 100644
index 000000000..4db85519c
--- /dev/null
+++ b/crypto/ext4crypt/Keymaster3.h
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_VOLD_KEYMASTER_H
+#define ANDROID_VOLD_KEYMASTER_H
+
+#ifdef __cplusplus
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include <android/hardware/keymaster/3.0/IKeymasterDevice.h>
+#include <keystore/authorization_set.h>
+#include "Utils.h"
+
+namespace android {
+namespace vold {
+using ::android::hardware::keymaster::V3_0::IKeymasterDevice;
+using ::keystore::ErrorCode;
+using ::keystore::KeyPurpose;
+using ::keystore::AuthorizationSet;
+
+// C++ wrappers to the Keymaster hidl interface.
+// This is tailored to the needs of KeyStorage, but could be extended to be
+// a more general interface.
+
+// Wrapper for a Keymaster operation handle representing an
+// ongoing Keymaster operation. Aborts the operation
+// in the destructor if it is unfinished. Methods log failures
+// to LOG(ERROR).
+class KeymasterOperation {
+ public:
+ ~KeymasterOperation();
+ // Is this instance valid? This is false if creation fails, and becomes
+ // false on finish or if an update fails.
+ explicit operator bool() { return mError == ErrorCode::OK; }
+ ErrorCode errorCode() { return mError; }
+ // Call "update" repeatedly until all of the input is consumed, and
+ // concatenate the output. Return true on success.
+ bool updateCompletely(const std::string& input, std::string* output);
+ // Finish and write the output to this string, unless pointer is null.
+ bool finish(std::string* output);
+ // Move constructor
+ KeymasterOperation(KeymasterOperation&& rhs) {
+ mDevice = std::move(rhs.mDevice);
+ mOpHandle = std::move(rhs.mOpHandle);
+ mError = std::move(rhs.mError);
+ }
+ // Construct an object in an error state for error returns
+ KeymasterOperation()
+ : mDevice{nullptr}, mOpHandle{0},
+ mError {ErrorCode::UNKNOWN_ERROR} {}
+ // Move Assignment
+ KeymasterOperation& operator= (KeymasterOperation&& rhs) {
+ mDevice = std::move(rhs.mDevice);
+ mOpHandle = std::move(rhs.mOpHandle);
+ mError = std::move(rhs.mError);
+ rhs.mError = ErrorCode::UNKNOWN_ERROR;
+ rhs.mOpHandle = 0;
+ return *this;
+ }
+
+ private:
+ KeymasterOperation(const sp<IKeymasterDevice>& d, uint64_t h)
+ : mDevice{d}, mOpHandle{h}, mError {ErrorCode::OK} {}
+ KeymasterOperation(ErrorCode error)
+ : mDevice{nullptr}, mOpHandle{0},
+ mError {error} {}
+ sp<IKeymasterDevice> mDevice;
+ uint64_t mOpHandle;
+ ErrorCode mError;
+ DISALLOW_COPY_AND_ASSIGN(KeymasterOperation);
+ friend class Keymaster;
+};
+
+// Wrapper for a Keymaster device for methods that start a KeymasterOperation or are not
+// part of one.
+class Keymaster {
+ public:
+ Keymaster();
+ // false if we failed to open the keymaster device.
+ explicit operator bool() { return mDevice.get() != nullptr; }
+ // Generate a key in the keymaster from the given params.
+ //bool generateKey(const AuthorizationSet& inParams, std::string* key);
+ // If the keymaster supports it, permanently delete a key.
+ bool deleteKey(const std::string& key);
+ // Replace stored key blob in response to KM_ERROR_KEY_REQUIRES_UPGRADE.
+ bool upgradeKey(const std::string& oldKey, const AuthorizationSet& inParams,
+ std::string* newKey);
+ // Begin a new cryptographic operation, collecting output parameters if pointer is non-null
+ KeymasterOperation begin(KeyPurpose purpose, const std::string& key,
+ const AuthorizationSet& inParams, AuthorizationSet* outParams);
+ bool isSecure();
+
+ private:
+ sp<hardware::keymaster::V3_0::IKeymasterDevice> mDevice;
+ DISALLOW_COPY_AND_ASSIGN(Keymaster);
+};
+
+} // namespace vold
+} // namespace android
+
+#endif // __cplusplus
+
+
+/*
+ * The following functions provide C bindings to keymaster services
+ * needed by cryptfs scrypt. The compatibility check checks whether
+ * the keymaster implementation is considered secure, i.e., TEE backed.
+ * The create_key function generates an RSA key for signing.
+ * The sign_object function signes an object with the given keymaster
+ * key.
+ */
+__BEGIN_DECLS
+
+int keymaster_compatibility_cryptfs_scrypt();
+/*int keymaster_create_key_for_cryptfs_scrypt(uint32_t rsa_key_size,
+ uint64_t rsa_exponent,
+ uint32_t ratelimit,
+ uint8_t* key_buffer,
+ uint32_t key_buffer_size,
+ uint32_t* key_out_size);
+
+int keymaster_sign_object_for_cryptfs_scrypt(const uint8_t* key_blob,
+ size_t key_blob_size,
+ uint32_t ratelimit,
+ const uint8_t* object,
+ const size_t object_size,
+ uint8_t** signature_buffer,
+ size_t* signature_buffer_size);*/
+
+__END_DECLS
+
+#endif
diff --git a/crypto/ext4crypt/Utils.h b/crypto/ext4crypt/Utils.h
index 8d0445d89..aede20341 100644
--- a/crypto/ext4crypt/Utils.h
+++ b/crypto/ext4crypt/Utils.h
@@ -24,6 +24,14 @@
#include <vector>
#include <string>
+// DISALLOW_COPY_AND_ASSIGN disallows the copy and operator= functions. It goes in the private:
+// declarations in a class.
+#if !defined(DISALLOW_COPY_AND_ASSIGN)
+#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
+ TypeName(const TypeName&) = delete; \
+ void operator=(const TypeName&) = delete
+#endif
+
namespace android {
namespace vold {
diff --git a/crypto/ext4crypt/Weaver1.cpp b/crypto/ext4crypt/Weaver1.cpp
new file mode 100644
index 000000000..6d09ec995
--- /dev/null
+++ b/crypto/ext4crypt/Weaver1.cpp
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2017 Team Win Recovery Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* To the best of my knowledge there is no native implementation for
+ * Weaver so I made this by looking at the IWeaver.h file that gets
+ * compiled by the build system. I took the information from this header
+ * file and looked at keymaster source to get an idea of the proper way
+ * to write the functions.
+ */
+
+#include "Weaver1.h"
+
+//#include <android-base/logging.h>
+#include <keystore/keymaster_tags.h>
+#include <keystore/authorization_set.h>
+#include <keystore/keystore_hidl_support.h>
+
+#include <android/hardware/weaver/1.0/IWeaver.h>
+
+#include <iostream>
+#define ERROR 1
+#define LOG(x) std::cout
+
+using namespace android::hardware::weaver;
+using android::hardware::hidl_string;
+using ::android::hardware::weaver::V1_0::IWeaver;
+using ::android::hardware::weaver::V1_0::WeaverConfig;
+using ::android::hardware::weaver::V1_0::WeaverReadStatus;
+using ::android::hardware::weaver::V1_0::WeaverReadResponse;
+using ::android::hardware::weaver::V1_0::WeaverStatus;
+using ::android::hardware::Return;
+using ::android::sp;
+
+namespace android {
+namespace vold {
+
+Weaver::Weaver() {
+ mDevice = ::android::hardware::weaver::V1_0::IWeaver::getService();
+ GottenConfig = false;
+}
+
+bool Weaver::GetConfig() {
+ if (GottenConfig)
+ return true;
+
+ WeaverStatus status;
+ WeaverConfig cfg;
+
+ bool callbackCalled = false;
+ auto ret = mDevice->getConfig([&](WeaverStatus s, WeaverConfig c) {
+ callbackCalled = true;
+ status = s;
+ cfg = c;
+ });
+ if (ret.isOk() && callbackCalled && status == WeaverStatus::OK) {
+ config = cfg;
+ GottenConfig = true;
+ return true;
+ }
+ return false;
+}
+
+bool Weaver::GetSlots(uint32_t* slots) {
+ if (!GetConfig())
+ return false;
+ *slots = config.slots;
+ return true;
+}
+
+bool Weaver::GetKeySize(uint32_t* keySize) {
+ if (!GetConfig())
+ return false;
+ *keySize = config.keySize;
+ return true;
+}
+
+bool Weaver::GetValueSize(uint32_t* valueSize) {
+ if (!GetConfig())
+ return false;
+ *valueSize = config.valueSize;
+ return true;
+}
+
+// TODO: we should return more information about the status including time delays before the next retry
+bool Weaver::WeaverVerify(const uint32_t slot, const void* weaver_key, std::vector<uint8_t>* payload) {
+ bool callbackCalled = false;
+ WeaverReadStatus status;
+ std::vector<uint8_t> readValue;
+ uint32_t timeout;
+ uint32_t keySize;
+ if (!GetKeySize(&keySize))
+ return false;
+ std::vector<uint8_t> key;
+ key.resize(keySize);
+ uint32_t index = 0;
+ unsigned char* ptr = (unsigned char*)weaver_key;
+ for (index = 0; index < keySize; index++) {
+ key[index] = *ptr;
+ ptr++;
+ }
+ const auto readRet = mDevice->read(slot, key, [&](WeaverReadStatus s, WeaverReadResponse r) {
+ callbackCalled = true;
+ status = s;
+ readValue = r.value;
+ timeout = r.timeout;
+ });
+ if (readRet.isOk() && callbackCalled && status == WeaverReadStatus::OK && timeout == 0) {
+ *payload = readValue;
+ return true;
+ }
+ return false;
+}
+
+} // namespace vold
+} // namespace android
diff --git a/crypto/ext4crypt/Weaver1.h b/crypto/ext4crypt/Weaver1.h
new file mode 100644
index 000000000..22f401e70
--- /dev/null
+++ b/crypto/ext4crypt/Weaver1.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2017 Team Win Recovery Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* To the best of my knowledge there is no native implementation for
+ * Weaver so I made this by looking at the IWeaver.h file that gets
+ * compiled by the build system. I took the information from this header
+ * file and looked at keymaster source to get an idea of the proper way
+ * to write the functions.
+ */
+
+#ifndef TWRP_WEAVER_H
+#define TWRP_WEAVER_H
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include <android/hardware/weaver/1.0/IWeaver.h>
+#include "Utils.h"
+
+namespace android {
+namespace vold {
+using ::android::hardware::weaver::V1_0::IWeaver;
+
+// Wrapper for a Weaver device
+class Weaver {
+ public:
+ Weaver();
+ // false if we failed to open the weaver device.
+ explicit operator bool() { return mDevice.get() != nullptr; }
+
+ bool GetSlots(uint32_t* slots);
+ bool GetKeySize(uint32_t* keySize);
+ bool GetValueSize(uint32_t* valueSize);
+ // TODO: we should return more information about the status including time delays before the next retry
+ bool WeaverVerify(const uint32_t slot, const void* weaver_key, std::vector<uint8_t>* payload);
+
+ private:
+ sp<hardware::weaver::V1_0::IWeaver> mDevice;
+ hardware::weaver::V1_0::WeaverConfig config;
+ bool GottenConfig;
+
+ bool GetConfig();
+
+ DISALLOW_COPY_AND_ASSIGN(Weaver);
+};
+
+} // namespace vold
+} // namespace android
+
+#endif
diff --git a/crypto/ext4crypt/e4policyget.cpp b/crypto/ext4crypt/e4policyget.cpp
index d217f18ee..05de86fe7 100644
--- a/crypto/ext4crypt/e4policyget.cpp
+++ b/crypto/ext4crypt/e4policyget.cpp
@@ -28,13 +28,10 @@ int main(int argc, char *argv[]) {
printf("Must specify a path\n");
return -1;
} else {
- char e4crypt_policy[EXT4_KEY_DESCRIPTOR_SIZE];
- if (e4crypt_policy_get(argv[1], e4crypt_policy, EXT4_KEY_DESCRIPTOR_SIZE, 0))
- {
- char* ptr = tar_policy;
- memset(tar_policy, 0, sizeof(tar_policy));
+ ext4_encryption_policy eep;
+ if (e4crypt_policy_get_struct(argv[1], &eep, sizeof(eep))) {
char policy_hex[EXT4_KEY_DESCRIPTOR_SIZE_HEX];
- policy_to_hex(e4crypt_policy, policy_hex);
+ policy_to_hex(eep.master_key_descriptor, policy_hex);
printf("%s\n", policy_hex);
} else {
printf("No policy set\n");
diff --git a/crypto/ext4crypt/ext4_crypt.cpp b/crypto/ext4crypt/ext4_crypt.cpp
index 029db7567..5a3b4b20c 100644
--- a/crypto/ext4crypt/ext4_crypt.cpp
+++ b/crypto/ext4crypt/ext4_crypt.cpp
@@ -22,6 +22,7 @@
*/
#include "ext4_crypt.h"
+#include "ext4crypt_tar.h"
#include <dirent.h>
#include <errno.h>
@@ -41,29 +42,13 @@
#define XATTR_NAME_ENCRYPTION_POLICY "encryption.policy"
#define EXT4_KEYREF_DELIMITER ((char)'.')
-// ext4enc:TODO Include structure from somewhere sensible
-// MUST be in sync with ext4_crypto.c in kernel
-#define EXT4_KEY_DESCRIPTOR_SIZE 8
-#define EXT4_KEY_DESCRIPTOR_SIZE_HEX 17
-
-struct ext4_encryption_policy {
- char version;
- char contents_encryption_mode;
- char filenames_encryption_mode;
- char flags;
- char master_key_descriptor[EXT4_KEY_DESCRIPTOR_SIZE];
-} __attribute__((__packed__));
-
#define EXT4_ENCRYPTION_MODE_AES_256_XTS 1
#define EXT4_ENCRYPTION_MODE_AES_256_CTS 4
+#define EXT4_ENCRYPTION_MODE_AES_256_HEH 126
#define EXT4_ENCRYPTION_MODE_PRIVATE 127
static int encryption_mode = EXT4_ENCRYPTION_MODE_PRIVATE;
-// ext4enc:TODO Get value from somewhere sensible
-#define EXT4_IOC_SET_ENCRYPTION_POLICY _IOR('f', 19, struct ext4_encryption_policy)
-#define EXT4_IOC_GET_ENCRYPTION_POLICY _IOW('f', 21, struct ext4_encryption_policy)
-
#define HEX_LOOKUP "0123456789abcdef"
extern "C" void policy_to_hex(const char* policy, char* hex) {
@@ -146,6 +131,48 @@ extern "C" bool e4crypt_policy_get(const char *directory, char *policy,
return true;
}
+extern "C" void e4crypt_policy_fill_default_struct(ext4_encryption_policy *eep) {
+ eep->version = 0;
+ eep->contents_encryption_mode = encryption_mode;
+ eep->filenames_encryption_mode = EXT4_ENCRYPTION_MODE_AES_256_CTS;
+ eep->flags = 0;
+ memset((void*)&eep->master_key_descriptor[0], 0, EXT4_KEY_DESCRIPTOR_SIZE);
+}
+
+extern "C" bool e4crypt_policy_set_struct(const char *directory, const ext4_encryption_policy *eep) {
+ int fd = open(directory, O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC);
+ if (fd == -1) {
+ printf("failed to open %s\n", directory);
+ PLOG(ERROR) << "Failed to open directory " << directory;
+ return false;
+ }
+ if (ioctl(fd, EXT4_IOC_SET_ENCRYPTION_POLICY, eep)) {
+ printf("failed to set policy for '%s'\n", directory);
+ PLOG(ERROR) << "Failed to set encryption policy for " << directory;
+ close(fd);
+ return false;
+ }
+ close(fd);
+ return true;
+}
+
+extern "C" bool e4crypt_policy_get_struct(const char *directory, ext4_encryption_policy *eep) {
+ int fd = open(directory, O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC);
+ if (fd == -1) {
+ printf("Failed to open '%s'\n", directory);
+ PLOG(ERROR) << "Failed to open directory " << directory;
+ return false;
+ }
+ memset(eep, 0, sizeof(ext4_encryption_policy));
+ if (ioctl(fd, EXT4_IOC_GET_ENCRYPTION_POLICY, eep) != 0) {
+ PLOG(ERROR) << "Failed to get encryption policy for " << directory;
+ close(fd);
+ return false;
+ }
+ close(fd);
+ return true;
+}
+
extern "C" bool e4crypt_set_mode() {
const char* mode_file = "/data/unencrypted/mode";
struct stat st;
diff --git a/crypto/ext4crypt/ext4crypt_tar.h b/crypto/ext4crypt/ext4crypt_tar.h
index 1c9cef0a5..c35d11571 100644
--- a/crypto/ext4crypt/ext4crypt_tar.h
+++ b/crypto/ext4crypt/ext4crypt_tar.h
@@ -21,8 +21,25 @@
#include <stdbool.h>
#include <cutils/multiuser.h>
+// ext4enc:TODO Include structure from somewhere sensible
+// MUST be in sync with ext4_crypto.c in kernel
+#define EXT4_KEY_DESCRIPTOR_SIZE 8
+#define EXT4_KEY_DESCRIPTOR_SIZE_HEX 17
+
+// ext4enc:TODO Get value from somewhere sensible
+#define EXT4_IOC_SET_ENCRYPTION_POLICY _IOR('f', 19, struct ext4_encryption_policy)
+#define EXT4_IOC_GET_ENCRYPTION_POLICY _IOW('f', 21, struct ext4_encryption_policy)
+
__BEGIN_DECLS
+struct ext4_encryption_policy {
+ char version;
+ char contents_encryption_mode;
+ char filenames_encryption_mode;
+ char flags;
+ char master_key_descriptor[EXT4_KEY_DESCRIPTOR_SIZE];
+} __attribute__((__packed__));
+
bool lookup_ref_key(const char* policy, char* policy_type);
bool lookup_ref_tar(const char* policy_type, char* policy);
@@ -31,6 +48,9 @@ bool e4crypt_policy_set(const char *directory, const char *policy,
size_t policy_length, int contents_encryption_mode);
bool e4crypt_policy_get(const char *directory, char *policy,
size_t policy_length, int contents_encryption_mode);
+void e4crypt_policy_fill_default_struct(struct ext4_encryption_policy *eep);
+bool e4crypt_policy_set_struct(const char *directory, const struct ext4_encryption_policy *eep);
+bool e4crypt_policy_get_struct(const char *directory, struct ext4_encryption_policy *eep);
bool e4crypt_set_mode();
__END_DECLS