summaryrefslogtreecommitdiffstats
path: root/crypto
diff options
context:
space:
mode:
Diffstat (limited to 'crypto')
-rw-r--r--crypto/ext4crypt/Android.mk61
-rw-r--r--crypto/ext4crypt/Decrypt.cpp970
-rw-r--r--crypto/ext4crypt/Decrypt.h34
-rw-r--r--crypto/ext4crypt/Ext4Crypt.cpp533
-rw-r--r--crypto/ext4crypt/Ext4Crypt.h47
-rw-r--r--crypto/ext4crypt/HashPassword.cpp84
-rw-r--r--crypto/ext4crypt/HashPassword.h34
-rw-r--r--crypto/ext4crypt/KeyStorage.cpp349
-rw-r--r--crypto/ext4crypt/KeyStorage.h53
-rw-r--r--crypto/ext4crypt/KeyStorage3.cpp526
-rw-r--r--crypto/ext4crypt/KeyStorage3.h58
-rw-r--r--crypto/ext4crypt/Keymaster.cpp254
-rw-r--r--crypto/ext4crypt/Keymaster.h110
-rw-r--r--crypto/ext4crypt/Keymaster3.cpp324
-rw-r--r--crypto/ext4crypt/Keymaster3.h148
-rw-r--r--crypto/ext4crypt/ScryptParameters.cpp50
-rw-r--r--crypto/ext4crypt/ScryptParameters.h32
-rw-r--r--crypto/ext4crypt/Utils.cpp297
-rw-r--r--crypto/ext4crypt/Utils.h73
-rw-r--r--crypto/ext4crypt/Weaver1.cpp128
-rw-r--r--crypto/ext4crypt/Weaver1.h64
-rw-r--r--crypto/ext4crypt/e4policyget.cpp41
-rw-r--r--crypto/ext4crypt/ext4_crypt.cpp207
-rw-r--r--crypto/ext4crypt/ext4crypt_tar.h58
-rw-r--r--crypto/ext4crypt/main.cpp36
-rw-r--r--crypto/lollipop/Android.mk60
-rw-r--r--crypto/lollipop/cryptfs.c1609
-rw-r--r--crypto/lollipop/cryptfs.h229
-rw-r--r--crypto/lollipop/main.c32
-rw-r--r--crypto/scrypt/Android.mk13
-rw-r--r--crypto/scrypt/MODULE_LICENSE_BSD_LIKE0
-rw-r--r--crypto/scrypt/NOTICE36
-rw-r--r--crypto/scrypt/Scrypt-config.mk105
-rw-r--r--crypto/scrypt/Scrypt.mk46
-rw-r--r--crypto/scrypt/android-config.mk16
-rw-r--r--crypto/scrypt/build-config.mk6
-rw-r--r--crypto/scrypt/config.h99
-rwxr-xr-xcrypto/scrypt/import_scrypt.sh493
-rw-r--r--crypto/scrypt/lib/README6
-rw-r--r--crypto/scrypt/lib/crypto/crypto_scrypt-neon-salsa208.h120
-rw-r--r--crypto/scrypt/lib/crypto/crypto_scrypt-neon.c305
-rw-r--r--crypto/scrypt/lib/crypto/crypto_scrypt-ref.c296
-rw-r--r--crypto/scrypt/lib/crypto/crypto_scrypt-sse.c378
-rw-r--r--crypto/scrypt/lib/crypto/crypto_scrypt.h46
-rw-r--r--crypto/scrypt/lib/util/sysendian.h140
-rw-r--r--crypto/scrypt/patches/README11
-rw-r--r--crypto/scrypt/patches/arm-neon.patch437
-rw-r--r--crypto/scrypt/patches/use_openssl_pbkdf2.patch80
-rw-r--r--crypto/scrypt/scrypt.config94
-rw-r--r--crypto/scrypt/scrypt.version1
-rw-r--r--crypto/scrypt/scrypt_platform.h12
-rw-r--r--crypto/scrypt/tests/Android.mk26
-rw-r--r--crypto/scrypt/tests/scrypt_test.cpp78
-rw-r--r--crypto/vold_decrypt/Android.mk99
-rwxr-xr-xcrypto/vold_decrypt/init.recovery.vold_decrypt.hwservicemanager.rc18
-rwxr-xr-xcrypto/vold_decrypt/init.recovery.vold_decrypt.keymaster-3-0.rc17
-rwxr-xr-xcrypto/vold_decrypt/init.recovery.vold_decrypt.qseecomd.rc26
-rwxr-xr-xcrypto/vold_decrypt/init.recovery.vold_decrypt.rc10
-rw-r--r--crypto/vold_decrypt/vold_decrypt.cpp1126
-rw-r--r--crypto/vold_decrypt/vold_decrypt.h42
60 files changed, 10683 insertions, 0 deletions
diff --git a/crypto/ext4crypt/Android.mk b/crypto/ext4crypt/Android.mk
new file mode 100644
index 000000000..af5ab3af0
--- /dev/null
+++ b/crypto/ext4crypt/Android.mk
@@ -0,0 +1,61 @@
+LOCAL_PATH := $(call my-dir)
+ifeq ($(TW_INCLUDE_CRYPTO), true)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libe4crypt
+LOCAL_MODULE_TAGS := eng optional
+LOCAL_CFLAGS :=
+LOCAL_SRC_FILES := Decrypt.cpp Ext4Crypt.cpp 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
+
+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_GATEKEEPER1
+ LOCAL_SRC_FILES += Keymaster3.cpp KeyStorage3.cpp
+ LOCAL_SHARED_LIBRARIES += android.hardware.keymaster@3.0 libkeystore_binder libhidlbase libutils libbinder
+ LOCAL_SHARED_LIBRARIES += android.hardware.gatekeeper@1.0
+ ifneq ($(wildcard hardware/interfaces/weaver/Android.bp),)
+ LOCAL_CFLAGS += -DHAVE_SYNTH_PWD_SUPPORT
+ LOCAL_SRC_FILES += Weaver1.cpp
+ LOCAL_SHARED_LIBRARIES += android.hardware.weaver@1.0
+ endif
+ ifneq ($(wildcard system/core/libkeyutils/Android.bp),)
+ LOCAL_CFLAGS += -DHAVE_LIBKEYUTILS
+ LOCAL_SHARED_LIBRARIES += libkeyutils
+ endif
+else
+ LOCAL_SRC_FILES += Keymaster.cpp KeyStorage.cpp
+endif
+
+include $(BUILD_SHARED_LIBRARY)
+
+
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := twrpfbe
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
+LOCAL_SRC_FILES := main.cpp
+LOCAL_SHARED_LIBRARIES := libe4crypt
+#LOCAL_LDFLAGS += -Wl,-dynamic-linker,/sbin/linker64
+
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := e4policyget
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
+LOCAL_SRC_FILES := e4policyget.cpp
+LOCAL_SHARED_LIBRARIES := libe4crypt
+LOCAL_LDFLAGS += -Wl,-dynamic-linker,/sbin/linker64
+
+include $(BUILD_EXECUTABLE)
+
+endif
diff --git a/crypto/ext4crypt/Decrypt.cpp b/crypto/ext4crypt/Decrypt.cpp
new file mode 100644
index 000000000..2dab16646
--- /dev/null
+++ b/crypto/ext4crypt/Decrypt.cpp
@@ -0,0 +1,970 @@
+/*
+ * Copyright (C) 2016 The Team Win Recovery Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Decrypt.h"
+#include "Ext4Crypt.h"
+
+#include <map>
+#include <string>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#ifndef HAVE_LIBKEYUTILS
+#include "key_control.h"
+#else
+#include <keyutils.h>
+#endif
+
+#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>
+
+// Store main DE raw ref / policy
+extern std::string de_raw_ref;
+extern std::map<userid_t, std::string> s_de_key_raw_refs;
+extern std::map<userid_t, std::string> s_ce_key_raw_refs;
+
+static bool lookup_ref_key_internal(std::map<userid_t, std::string>& key_map, const char* policy, userid_t* user_id) {
+ for (std::map<userid_t, std::string>::iterator it=key_map.begin(); it!=key_map.end(); ++it) {
+ if (strncmp(it->second.c_str(), policy, it->second.size()) == 0) {
+ *user_id = it->first;
+ return true;
+ }
+ }
+ return false;
+}
+
+extern "C" bool lookup_ref_key(const char* policy, char* policy_type) {
+ userid_t user_id = 0;
+ if (strncmp(de_raw_ref.c_str(), policy, de_raw_ref.size()) == 0) {
+ strcpy(policy_type, "1DK");
+ return true;
+ }
+ if (!lookup_ref_key_internal(s_de_key_raw_refs, policy, &user_id)) {
+ if (!lookup_ref_key_internal(s_ce_key_raw_refs, policy, &user_id)) {
+ return false;
+ } else
+ sprintf(policy_type, "1CE%d", user_id);
+ } else
+ sprintf(policy_type, "1DE%d", user_id);
+ return true;
+}
+
+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[0]);
+ return false;
+ }
+ const char* ptr = policy_type + 1; // skip past the version number
+ if (strncmp(ptr, "DK", 2) == 0) {
+ strncpy(policy, de_raw_ref.data(), de_raw_ref.size());
+ return true;
+ }
+ userid_t user_id = atoi(ptr + 2);
+ std::string raw_ref;
+ if (*ptr == 'D') {
+ if (lookup_key_ref(s_de_key_raw_refs, user_id, &raw_ref)) {
+ strncpy(policy, raw_ref.data(), raw_ref.size());
+ } else
+ return false;
+ } else if (*ptr == 'C') {
+ if (lookup_key_ref(s_ce_key_raw_refs, user_id, &raw_ref)) {
+ strncpy(policy, raw_ref.data(), raw_ref.size());
+ } else
+ return false;
+ } else {
+ printf("unknown policy type '%s'\n", policy_type);
+ return false;
+ }
+ return true;
+}
+
+#ifndef HAVE_GATEKEEPER1
+int gatekeeper_device_initialize(gatekeeper_device_t **dev) {
+ int ret;
+ const hw_module_t *mod;
+ ret = hw_get_module_by_class(GATEKEEPER_HARDWARE_MODULE_ID, NULL, &mod);
+
+ if (ret!=0) {
+ printf("failed to get hw module\n");
+ return ret;
+ }
+
+ ret = gatekeeper_open(mod, dev);
+
+ if (ret!=0)
+ printf("failed to open gatekeeper\n");
+ return ret;
+}
+#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/";
+ } else {
+ char user_id_str[5];
+ sprintf(user_id_str, "%i", user_id);
+ path = "/data/system/users/";
+ path += user_id_str;
+ path += "/";
+ }
+ filename = path + "gatekeeper.password.key";
+ if (stat(filename.c_str(), &st) == 0 && st.st_size > 0)
+ return 1;
+ filename = path + "gatekeeper.pattern.key";
+ if (stat(filename.c_str(), &st) == 0 && st.st_size > 0)
+ return 2;
+ printf("Unable to locate gatekeeper password file '%s'\n", filename.c_str());
+ filename = "";
+ return 0;
+}
+
+bool Decrypt_User(const userid_t user_id, const std::string& Password) {
+ uint8_t *auth_token;
+ uint32_t auth_token_len;
+ int ret;
+
+ struct stat st;
+ if (user_id > 9999) {
+ printf("user_id is too big\n");
+ return false;
+ }
+ std::string filename;
+ bool Default_Password = (Password == "!");
+ if (Get_Password_Type(user_id, filename) == 0 && !Default_Password) {
+ printf("Unknown password type\n");
+ return false;
+ }
+ int flags = FLAG_STORAGE_DE;
+ if (user_id == 0)
+ flags = FLAG_STORAGE_DE;
+ else
+ flags = FLAG_STORAGE_CE;
+ if (Default_Password) {
+ if (!e4crypt_unlock_user_key(user_id, 0, "!", "!")) {
+ printf("e4crypt_unlock_user_key returned fail\n");
+ return false;
+ }
+ if (!e4crypt_prepare_user_storage(nullptr, user_id, 0, flags)) {
+ printf("failed to e4crypt_prepare_user_storage\n");
+ return false;
+ }
+ printf("Decrypted Successfully!\n");
+ return true;
+ }
+ if (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));
+ return false;
+ }
+ std::string handle;
+ if (!android::base::ReadFileToString(filename, &handle)) {
+ printf("Failed to read '%s'\n", filename.c_str());
+ return false;
+ }
+ bool should_reenroll;
+#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;
+ for (i=0;i<auth_token_len;i++) {
+ sprintf(&token_hex[2*i], "%02X", auth_token[i]);
+ }
+ // The secret is "Android FBE credential hash" plus appended 0x00 to reach 128 bytes then append the user's password then feed that to sha512sum
+ std::string secret = HashPassword(Password);
+ if (!e4crypt_unlock_user_key(user_id, 0, token_hex, secret.c_str())) {
+ printf("e4crypt_unlock_user_key returned fail\n");
+ return false;
+ }
+ if (!e4crypt_prepare_user_storage(nullptr, user_id, 0, flags)) {
+ printf("failed to e4crypt_prepare_user_storage\n");
+ return false;
+ }
+ printf("Decrypted Successfully!\n");
+ return true;
+}
diff --git a/crypto/ext4crypt/Decrypt.h b/crypto/ext4crypt/Decrypt.h
new file mode 100644
index 000000000..c05ac69b9
--- /dev/null
+++ b/crypto/ext4crypt/Decrypt.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdbool.h>
+#include <sys/cdefs.h>
+
+#include <cutils/multiuser.h>
+
+#include <string>
+
+__BEGIN_DECLS
+
+// NOTE: keep in sync with StorageManager
+static constexpr int FLAG_STORAGE_DE = 1 << 0;
+static constexpr int FLAG_STORAGE_CE = 1 << 1;
+
+int Get_Password_Type(const userid_t user_id, std::string& filename);
+bool Decrypt_DE();
+bool Decrypt_User(const userid_t user_id, const std::string& Password);
+
+__END_DECLS
diff --git a/crypto/ext4crypt/Ext4Crypt.cpp b/crypto/ext4crypt/Ext4Crypt.cpp
new file mode 100644
index 000000000..ea5b1cf88
--- /dev/null
+++ b/crypto/ext4crypt/Ext4Crypt.cpp
@@ -0,0 +1,533 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Ext4Crypt.h"
+#include "Decrypt.h"
+
+#include "KeyStorage.h"
+#include "Utils.h"
+
+#include <algorithm>
+#include <iomanip>
+#include <map>
+#include <set>
+#include <sstream>
+#include <string>
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <openssl/sha.h>
+#include <selinux/android.h>
+#include <stdio.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <iostream>
+
+#include <private/android_filesystem_config.h>
+
+#ifdef HAVE_SYNTH_PWD_SUPPORT
+#include <ext4_utils/ext4_crypt.h>
+#else
+#include "ext4_crypt.h"
+#endif
+#ifndef HAVE_LIBKEYUTILS
+#include "key_control.h"
+#else
+#include <keyutils.h>
+#endif
+
+#include <hardware/gatekeeper.h>
+#include "HashPassword.h"
+
+#define EMULATED_USES_SELINUX 0
+#define MANAGE_MISC_DIRS 0
+
+#include <cutils/fs.h>
+
+#include <android-base/file.h>
+//#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+
+#define LOG(x) std::cout
+#define PLOG(x) std::cout
+#define DATA_MNT_POINT "/data"
+
+using android::base::StringPrintf;
+using android::vold::kEmptyAuthentication;
+
+// NOTE: keep in sync with StorageManager
+//static constexpr int FLAG_STORAGE_DE = 1 << 0; // moved to Decrypt.h
+//static constexpr int FLAG_STORAGE_CE = 1 << 1;
+
+// Store main DE raw ref / policy
+std::string de_raw_ref;
+// Map user ids to key references
+std::map<userid_t, std::string> s_de_key_raw_refs;
+std::map<userid_t, std::string> s_ce_key_raw_refs;
+
+namespace {
+const std::string device_key_dir = std::string() + DATA_MNT_POINT + e4crypt_unencrypted_folder;
+const std::string device_key_path = device_key_dir + "/key";
+const std::string device_key_temp = device_key_dir + "/temp";
+
+const std::string user_key_dir = std::string() + DATA_MNT_POINT + "/misc/vold/user_keys";
+const std::string user_key_temp = user_key_dir + "/temp";
+
+bool s_global_de_initialized = false;
+
+// Some users are ephemeral, don't try to wipe their keys from disk
+std::set<userid_t> s_ephemeral_users;
+
+// TODO abolish this map. Keys should not be long-lived in user memory, only kernel memory.
+// See b/26948053
+std::map<userid_t, std::string> s_ce_keys;
+
+// ext4enc:TODO get this const from somewhere good
+const int EXT4_KEY_DESCRIPTOR_SIZE = 8;
+
+// ext4enc:TODO Include structure from somewhere sensible
+// MUST be in sync with ext4_crypto.c in kernel
+constexpr int EXT4_ENCRYPTION_MODE_AES_256_XTS = 1;
+constexpr int EXT4_AES_256_XTS_KEY_SIZE = 64;
+constexpr int EXT4_MAX_KEY_SIZE = 64;
+struct ext4_encryption_key {
+ uint32_t mode;
+ char raw[EXT4_MAX_KEY_SIZE];
+ uint32_t size;
+};
+}
+
+static bool e4crypt_is_emulated() {
+ return false; //property_get_bool("persist.sys.emulate_fbe", false);
+}
+
+static const char* escape_null(const char* value) {
+ return (value == nullptr) ? "null" : value;
+}
+
+// Get raw keyref - used to make keyname and to pass to ioctl
+static std::string generate_key_ref(const char* key, int length) {
+ SHA512_CTX c;
+
+ SHA512_Init(&c);
+ SHA512_Update(&c, key, length);
+ unsigned char key_ref1[SHA512_DIGEST_LENGTH];
+ SHA512_Final(key_ref1, &c);
+
+ SHA512_Init(&c);
+ SHA512_Update(&c, key_ref1, SHA512_DIGEST_LENGTH);
+ unsigned char key_ref2[SHA512_DIGEST_LENGTH];
+ SHA512_Final(key_ref2, &c);
+
+ static_assert(EXT4_KEY_DESCRIPTOR_SIZE <= SHA512_DIGEST_LENGTH,
+ "Hash too short for descriptor");
+ return std::string((char*)key_ref2, EXT4_KEY_DESCRIPTOR_SIZE);
+}
+
+static bool fill_key(const std::string& key, ext4_encryption_key* ext4_key) {
+ if (key.size() != EXT4_AES_256_XTS_KEY_SIZE) {
+ LOG(ERROR) << "Wrong size key " << key.size();
+ return false;
+ }
+ static_assert(EXT4_AES_256_XTS_KEY_SIZE <= sizeof(ext4_key->raw), "Key too long!");
+ ext4_key->mode = EXT4_ENCRYPTION_MODE_AES_256_XTS;
+ ext4_key->size = key.size();
+ memset(ext4_key->raw, 0, sizeof(ext4_key->raw));
+ memcpy(ext4_key->raw, key.data(), key.size());
+ return true;
+}
+
+static std::string keyname(const std::string& raw_ref) {
+ std::ostringstream o;
+ o << "ext4:";
+ for (auto i : raw_ref) {
+ o << std::hex << std::setw(2) << std::setfill('0') << (int)i;
+ }
+ LOG(INFO) << "keyname is " << o.str() << "\n";
+ return o.str();
+}
+
+// Get the keyring we store all keys in
+static bool e4crypt_keyring(key_serial_t* device_keyring) {
+ *device_keyring = keyctl_search(KEY_SPEC_SESSION_KEYRING, "keyring", "e4crypt", 0);
+ if (*device_keyring == -1) {
+ PLOG(ERROR) << "Unable to find device keyring\n";
+ return false;
+ }
+ return true;
+}
+
+// Install password into global keyring
+// Return raw key reference for use in policy
+static bool install_key(const std::string& key, std::string* raw_ref) {
+ ext4_encryption_key ext4_key;
+ if (!fill_key(key, &ext4_key)) return false;
+ *raw_ref = generate_key_ref(ext4_key.raw, ext4_key.size);
+ auto ref = keyname(*raw_ref);
+ key_serial_t device_keyring;
+ if (!e4crypt_keyring(&device_keyring)) return false;
+ key_serial_t key_id =
+ add_key("logon", ref.c_str(), (void*)&ext4_key, sizeof(ext4_key), device_keyring);
+ if (key_id == -1) {
+ PLOG(ERROR) << "Failed to insert key into keyring " << device_keyring << "\n";
+ return false;
+ }
+ LOG(DEBUG) << "Added key " << key_id << " (" << ref << ") to keyring " << device_keyring
+ << " in process " << getpid() << "\n";
+ return true;
+}
+
+static std::string get_de_key_path(userid_t user_id) {
+LOG(INFO) << "get_de_key_path " << user_id << " " << StringPrintf("%s/de/%d", user_key_dir.c_str(), user_id) << "\n";
+ return StringPrintf("%s/de/%d", user_key_dir.c_str(), user_id);
+}
+
+static std::string get_ce_key_directory_path(userid_t user_id) {
+LOG(INFO) << "get_ce_key_directory_path " << user_id << ": " << StringPrintf("%s/ce/%d", user_key_dir.c_str(), user_id) << "\n";
+ return StringPrintf("%s/ce/%d", user_key_dir.c_str(), user_id);
+}
+
+// Returns the keys newest first
+static std::vector<std::string> get_ce_key_paths(const std::string& directory_path) {
+ auto dirp = std::unique_ptr<DIR, int (*)(DIR*)>(opendir(directory_path.c_str()), closedir);
+ if (!dirp) {
+ PLOG(ERROR) << "Unable to open ce key directory: " + directory_path;
+ return std::vector<std::string>();
+ }
+ std::vector<std::string> result;
+ for (;;) {
+ errno = 0;
+ auto const entry = readdir(dirp.get());
+ if (!entry) {
+ if (errno) {
+ PLOG(ERROR) << "Unable to read ce key directory: " + directory_path;
+ return std::vector<std::string>();
+ }
+ break;
+ }
+ if (entry->d_type != DT_DIR || entry->d_name[0] != 'c') {
+ LOG(DEBUG) << "Skipping non-key " << entry->d_name;
+ continue;
+ }
+ result.emplace_back(directory_path + "/" + entry->d_name);
+ LOG(INFO) << "get_ce_key_paths adding: " << directory_path + "/" + entry->d_name << "\n";
+ }
+ std::sort(result.begin(), result.end());
+ std::reverse(result.begin(), result.end());
+ return result;
+}
+
+static std::string get_ce_key_current_path(const std::string& directory_path) {
+LOG(INFO) << "get_ce_key_current_path: " << directory_path + "/current\n";
+ return directory_path + "/current";
+}
+
+// Discard all keys but the named one; rename it to canonical name.
+// No point in acting on errors in this; ignore them.
+static void fixate_user_ce_key(const std::string& directory_path, const std::string &to_fix,
+ const std::vector<std::string>& paths) {
+ for (auto const other_path: paths) {
+ if (other_path != to_fix) {
+ android::vold::destroyKey(other_path);
+ }
+ }
+ auto const current_path = get_ce_key_current_path(directory_path);
+ if (to_fix != current_path) {
+ LOG(DEBUG) << "Renaming " << to_fix << " to " << current_path;
+ if (rename(to_fix.c_str(), current_path.c_str()) != 0) {
+ PLOG(WARNING) << "Unable to rename " << to_fix << " to " << current_path;
+ }
+ }
+}
+
+static bool read_and_fixate_user_ce_key(userid_t user_id,
+ const android::vold::KeyAuthentication& auth,
+ std::string *ce_key) {
+ auto const directory_path = get_ce_key_directory_path(user_id);
+ auto const paths = get_ce_key_paths(directory_path);
+ for (auto const ce_key_path: paths) {
+ LOG(DEBUG) << "Trying user CE key " << ce_key_path;
+ if (android::vold::retrieveKey(ce_key_path, auth, ce_key)) {
+ LOG(DEBUG) << "Successfully retrieved key";
+ fixate_user_ce_key(directory_path, ce_key_path, paths);
+ return true;
+ }
+ }
+ LOG(ERROR) << "Failed to find working ce key for user " << user_id;
+ return false;
+}
+
+static bool read_and_install_user_ce_key(userid_t user_id,
+ const android::vold::KeyAuthentication& auth) {
+ if (s_ce_key_raw_refs.count(user_id) != 0) return true;
+ std::string ce_key;
+ if (!read_and_fixate_user_ce_key(user_id, auth, &ce_key)) return false;
+ std::string ce_raw_ref;
+ if (!install_key(ce_key, &ce_raw_ref)) return false;
+ s_ce_keys[user_id] = ce_key;
+ s_ce_key_raw_refs[user_id] = ce_raw_ref;
+ LOG(DEBUG) << "Installed ce key for user " << user_id;
+ return true;
+}
+
+static bool prepare_dir(const std::string& dir, mode_t mode, uid_t uid, gid_t gid) {
+ LOG(DEBUG) << "Preparing: " << dir << "\n";
+ return true;
+ return access(dir.c_str(), F_OK) == 0; // we don't want recovery creating directories or changing permissions at this point, so we will just return true if the path already exists
+ if (fs_prepare_dir(dir.c_str(), mode, uid, gid) != 0) {
+ PLOG(ERROR) << "Failed to prepare " << dir;
+ return false;
+ }
+ return true;
+}
+
+static bool path_exists(const std::string& path) {
+ return access(path.c_str(), F_OK) == 0;
+}
+
+bool lookup_key_ref(const std::map<userid_t, std::string>& key_map, userid_t user_id,
+ std::string* raw_ref) {
+ auto refi = key_map.find(user_id);
+ if (refi == key_map.end()) {
+ LOG(ERROR) << "Cannot find key for " << user_id;
+ return false;
+ }
+ *raw_ref = refi->second;
+ return true;
+}
+
+static bool ensure_policy(const std::string& raw_ref, const std::string& path) {
+ LOG(INFO) << "ensure_policy '" << path << "'\n";
+ return true;
+ return access(path.c_str(), F_OK) == 0; // ensure policy will set a policy if one is not set on an empty folder - we don't want to do this in recovery
+ /*if (e4crypt_policy_ensure(path.c_str(), raw_ref.data(), raw_ref.size()) != 0) {
+ LOG(ERROR) << "Failed to set policy on: " << path << "\n";
+ return false;
+ }
+ return true;*/
+}
+
+static bool is_numeric(const char* name) {
+ for (const char* p = name; *p != '\0'; p++) {
+ if (!isdigit(*p)) return false;
+ }
+ return true;
+}
+
+static bool load_all_de_keys() {
+ auto de_dir = user_key_dir + "/de";
+ auto dirp = std::unique_ptr<DIR, int (*)(DIR*)>(opendir(de_dir.c_str()), closedir);
+ if (!dirp) {
+ PLOG(ERROR) << "Unable to read de key directory";
+ return false;
+ }
+ for (;;) {
+ errno = 0;
+ auto entry = readdir(dirp.get());
+ if (!entry) {
+ if (errno) {
+ PLOG(ERROR) << "Unable to read de key directory";
+ return false;
+ }
+ break;
+ }
+ if (entry->d_type != DT_DIR || !is_numeric(entry->d_name)) {
+ LOG(DEBUG) << "Skipping non-de-key " << entry->d_name;
+ continue;
+ }
+ userid_t user_id = atoi(entry->d_name);
+ if (s_de_key_raw_refs.count(user_id) == 0) {
+ auto key_path = de_dir + "/" + entry->d_name;
+ std::string key;
+ if (!android::vold::retrieveKey(key_path, kEmptyAuthentication, &key)) return false;
+ std::string raw_ref;
+ if (!install_key(key, &raw_ref)) return false;
+ s_de_key_raw_refs[user_id] = raw_ref;
+ LOG(DEBUG) << "Installed de key for user " << user_id;
+ }
+ }
+ // ext4enc:TODO: go through all DE directories, ensure that all user dirs have the
+ // correct policy set on them, and that no rogue ones exist.
+ return true;
+}
+
+bool e4crypt_initialize_global_de() {
+
+ if (s_global_de_initialized) {
+ LOG(INFO) << "Already initialized\n";
+ return true;
+ }
+
+ std::string device_key;
+ if (path_exists(device_key_path)) {
+ if (!android::vold::retrieveKey(device_key_path,
+ kEmptyAuthentication, &device_key)) return false;
+ } else {
+ LOG(INFO) << "NOT Creating new key\n";
+ return false;
+ }
+
+ std::string device_key_ref;
+ if (!install_key(device_key, &device_key_ref)) {
+ LOG(ERROR) << "Failed to install device key\n";
+ return false;
+ }
+
+ s_global_de_initialized = true;
+ de_raw_ref = device_key_ref;
+ return true;
+}
+
+bool e4crypt_init_user0() {
+ if (e4crypt_is_native()) {
+ if (!prepare_dir(user_key_dir, 0700, AID_ROOT, AID_ROOT)) return false;
+ if (!prepare_dir(user_key_dir + "/ce", 0700, AID_ROOT, AID_ROOT)) return false;
+ if (!prepare_dir(user_key_dir + "/de", 0700, AID_ROOT, AID_ROOT)) return false;
+ if (!path_exists(get_de_key_path(0))) {
+ //if (!create_and_install_user_keys(0, false)) return false;
+ printf("de key path not found\n");
+ return false;
+ }
+ // TODO: switch to loading only DE_0 here once framework makes
+ // explicit calls to install DE keys for secondary users
+ if (!load_all_de_keys()) return false;
+ }
+ // We can only safely prepare DE storage here, since CE keys are probably
+ // entangled with user credentials. The framework will always prepare CE
+ // storage once CE keys are installed.
+ if (!e4crypt_prepare_user_storage(nullptr, 0, 0, FLAG_STORAGE_DE)) {
+ LOG(ERROR) << "Failed to prepare user 0 storage";
+ return false;
+ }
+
+ // If this is a non-FBE device that recently left an emulated mode,
+ // restore user data directories to known-good state.
+ if (!e4crypt_is_native() && !e4crypt_is_emulated()) {
+ e4crypt_unlock_user_key(0, 0, "!", "!");
+ }
+
+ return true;
+}
+
+static bool parse_hex(const char* hex, std::string* result) {
+ if (strcmp("!", hex) == 0) {
+ *result = "";
+ return true;
+ }
+ if (android::vold::HexToStr(hex, *result) != 0) {
+ LOG(ERROR) << "Invalid FBE hex string"; // Don't log the string for security reasons
+ return false;
+ }
+ return true;
+}
+
+// TODO: rename to 'install' for consistency, and take flags to know which keys to install
+bool e4crypt_unlock_user_key(userid_t user_id, int serial, const char* token_hex,
+ const char* secret_hex) {
+ if (e4crypt_is_native()) {
+ if (s_ce_key_raw_refs.count(user_id) != 0) {
+ LOG(WARNING) << "Tried to unlock already-unlocked key for user " << user_id;
+ return true;
+ }
+ std::string token, secret;
+ if (!parse_hex(token_hex, &token)) return false;
+ if (!parse_hex(secret_hex, &secret)) return false;
+ android::vold::KeyAuthentication auth(token, secret);
+ if (!read_and_install_user_ce_key(user_id, auth)) {
+ LOG(ERROR) << "Couldn't read key for " << user_id;
+ return false;
+ }
+ } else {
+ printf("Emulation mode not supported in TWRP\n");
+ // When in emulation mode, we just use chmod. However, we also
+ // unlock directories when not in emulation mode, to bring devices
+ // back into a known-good state.
+ /*if (!emulated_unlock(android::vold::BuildDataSystemCePath(user_id), 0771) ||
+ !emulated_unlock(android::vold::BuildDataMiscCePath(user_id), 01771) ||
+ !emulated_unlock(android::vold::BuildDataMediaCePath(nullptr, user_id), 0770) ||
+ !emulated_unlock(android::vold::BuildDataUserCePath(nullptr, user_id), 0771)) {
+ LOG(ERROR) << "Failed to unlock user " << user_id;
+ return false;
+ }*/
+ }
+ return true;
+}
+
+bool e4crypt_prepare_user_storage(const char* volume_uuid, userid_t user_id, int serial,
+ int flags) {
+
+ if (flags & FLAG_STORAGE_DE) {
+ // DE_sys key
+ auto system_legacy_path = android::vold::BuildDataSystemLegacyPath(user_id);
+ auto misc_legacy_path = android::vold::BuildDataMiscLegacyPath(user_id);
+ auto profiles_de_path = android::vold::BuildDataProfilesDePath(user_id);
+ auto foreign_de_path = android::vold::BuildDataProfilesForeignDexDePath(user_id);
+
+ // DE_n key
+ auto system_de_path = android::vold::BuildDataSystemDePath(user_id);
+ auto misc_de_path = android::vold::BuildDataMiscDePath(user_id);
+ auto user_de_path = android::vold::BuildDataUserDePath(volume_uuid, user_id);
+
+ if (!prepare_dir(system_legacy_path, 0700, AID_SYSTEM, AID_SYSTEM)) return false;
+#if MANAGE_MISC_DIRS
+ if (!prepare_dir(misc_legacy_path, 0750, multiuser_get_uid(user_id, AID_SYSTEM),
+ multiuser_get_uid(user_id, AID_EVERYBODY))) return false;
+#endif
+ if (!prepare_dir(profiles_de_path, 0771, AID_SYSTEM, AID_SYSTEM)) return false;
+ if (!prepare_dir(foreign_de_path, 0773, AID_SYSTEM, AID_SYSTEM)) return false;
+
+ if (!prepare_dir(system_de_path, 0770, AID_SYSTEM, AID_SYSTEM)) return false;
+ if (!prepare_dir(misc_de_path, 01771, AID_SYSTEM, AID_MISC)) return false;
+ if (!prepare_dir(user_de_path, 0771, AID_SYSTEM, AID_SYSTEM)) return false;
+
+ // For now, FBE is only supported on internal storage
+ if (e4crypt_is_native() && volume_uuid == nullptr) {
+ std::string de_raw_ref;
+ if (!lookup_key_ref(s_de_key_raw_refs, user_id, &de_raw_ref)) return false;
+ if (!ensure_policy(de_raw_ref, system_de_path)) return false;
+ if (!ensure_policy(de_raw_ref, misc_de_path)) return false;
+ if (!ensure_policy(de_raw_ref, user_de_path)) return false;
+ }
+ }
+
+ if (flags & FLAG_STORAGE_CE) {
+ // CE_n key
+ auto system_ce_path = android::vold::BuildDataSystemCePath(user_id);
+ auto misc_ce_path = android::vold::BuildDataMiscCePath(user_id);
+ auto media_ce_path = android::vold::BuildDataMediaCePath(volume_uuid, user_id);
+ auto user_ce_path = android::vold::BuildDataUserCePath(volume_uuid, user_id);
+
+ if (!prepare_dir(system_ce_path, 0770, AID_SYSTEM, AID_SYSTEM)) return false;
+ if (!prepare_dir(misc_ce_path, 01771, AID_SYSTEM, AID_MISC)) return false;
+ if (!prepare_dir(media_ce_path, 0770, AID_MEDIA_RW, AID_MEDIA_RW)) return false;
+ if (!prepare_dir(user_ce_path, 0771, AID_SYSTEM, AID_SYSTEM)) return false;
+
+ // For now, FBE is only supported on internal storage
+ if (e4crypt_is_native() && volume_uuid == nullptr) {
+ std::string ce_raw_ref;
+ if (!lookup_key_ref(s_ce_key_raw_refs, user_id, &ce_raw_ref)) return false;
+ if (!ensure_policy(ce_raw_ref, system_ce_path)) return false;
+ if (!ensure_policy(ce_raw_ref, misc_ce_path)) return false;
+ if (!ensure_policy(ce_raw_ref, media_ce_path)) return false;
+ if (!ensure_policy(ce_raw_ref, user_ce_path)) return false;
+ }
+ }
+
+ return true;
+}
diff --git a/crypto/ext4crypt/Ext4Crypt.h b/crypto/ext4crypt/Ext4Crypt.h
new file mode 100644
index 000000000..57623e35c
--- /dev/null
+++ b/crypto/ext4crypt/Ext4Crypt.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdbool.h>
+#include <sys/cdefs.h>
+
+#include <cutils/multiuser.h>
+
+#include <map>
+#include <string>
+
+__BEGIN_DECLS
+
+// General functions
+bool e4crypt_is_native();
+bool e4crypt_initialize_global_de();
+
+bool e4crypt_init_user0();
+//bool e4crypt_vold_create_user_key(userid_t user_id, int serial, bool ephemeral);
+//bool e4crypt_destroy_user_key(userid_t user_id);
+//bool e4crypt_add_user_key_auth(userid_t user_id, int serial, const char* token,
+// const char* secret);
+//bool e4crypt_fixate_newest_user_key_auth(userid_t user_id);
+
+bool e4crypt_unlock_user_key(userid_t user_id, int serial, const char* token, const char* secret);
+//bool e4crypt_lock_user_key(userid_t user_id);
+
+bool e4crypt_prepare_user_storage(const char* volume_uuid, userid_t user_id, int serial, int flags);
+//bool e4crypt_destroy_user_storage(const char* volume_uuid, userid_t user_id, int flags);
+
+bool lookup_key_ref(const std::map<userid_t, std::string>& key_map, userid_t user_id,
+ std::string* raw_ref);
+
+__END_DECLS
diff --git a/crypto/ext4crypt/HashPassword.cpp b/crypto/ext4crypt/HashPassword.cpp
new file mode 100644
index 000000000..817c984bd
--- /dev/null
+++ b/crypto/ext4crypt/HashPassword.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2016 Team Win Recovery Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * This computes the "secret" used by Android as one of the parameters
+ * to decrypt File Based Encryption. The secret is prefixed with
+ * "Android FBE credential hash" padded with 0s to 128 bytes then the
+ * user's password is appended to the end of the 128 bytes. This string
+ * is then hashed with sha512 and the sha512 value is then converted to
+ * hex with upper-case characters.
+ */
+
+#include <stdio.h>
+#include <string>
+#include <stdlib.h>
+#include <openssl/sha.h>
+
+#include "HashPassword.h"
+
+#define PASS_PADDING_SIZE 128
+#define SHA512_HEX_SIZE SHA512_DIGEST_LENGTH * 2
+
+void* PersonalizedHashBinary(const char* prefix, const char* key, const size_t key_size) {
+ size_t size = PASS_PADDING_SIZE + key_size;
+ unsigned char* buffer = (unsigned char*)calloc(1, size);
+ if (!buffer) return NULL; // failed to malloc
+ memcpy((void*)buffer, (void*)prefix, strlen(prefix));
+ unsigned char* ptr = buffer + PASS_PADDING_SIZE;
+ memcpy((void*)ptr, key, key_size);
+ unsigned char hash[SHA512_DIGEST_LENGTH];
+ SHA512_CTX sha512;
+ SHA512_Init(&sha512);
+ SHA512_Update(&sha512, buffer, size);
+ SHA512_Final(hash, &sha512);
+ free(buffer);
+ void* ret = malloc(SHA512_DIGEST_LENGTH);
+ if (!ret) return NULL; // failed to malloc
+ memcpy(ret, (void*)&hash[0], SHA512_DIGEST_LENGTH);
+ return ret;
+}
+
+std::string PersonalizedHash(const char* prefix, const char* key, const size_t key_size) {
+ size_t size = PASS_PADDING_SIZE + key_size;
+ unsigned char* buffer = (unsigned char*)calloc(1, size);
+ if (!buffer) return ""; // failed to malloc
+ memcpy((void*)buffer, (void*)prefix, strlen(prefix));
+ unsigned char* ptr = buffer + PASS_PADDING_SIZE;
+ memcpy((void*)ptr, key, key_size);
+ unsigned char hash[SHA512_DIGEST_LENGTH];
+ SHA512_CTX sha512;
+ SHA512_Init(&sha512);
+ SHA512_Update(&sha512, buffer, size);
+ SHA512_Final(hash, &sha512);
+ int index = 0;
+ char hex_hash[SHA512_HEX_SIZE + 1];
+ for(index = 0; index < SHA512_DIGEST_LENGTH; index++)
+ sprintf(hex_hash + (index * 2), "%02X", hash[index]);
+ hex_hash[128] = 0;
+ std::string ret = hex_hash;
+ free(buffer);
+ return ret;
+}
+
+std::string PersonalizedHash(const char* prefix, const std::string& Password) {
+ return PersonalizedHash(prefix, Password.c_str(), Password.size());
+}
+
+std::string 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
new file mode 100644
index 000000000..8abd0de71
--- /dev/null
+++ b/crypto/ext4crypt/HashPassword.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2016 Team Win Recovery Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __HASH_PASSWORD_H
+#define __HASH_PASSWORD_H
+
+#include <string>
+
+#define FBE_PERSONALIZATION "Android FBE credential hash"
+#define PERSONALISATION_WEAVER_KEY "weaver-key"
+#define PERSONALISATION_WEAVER_PASSWORD "weaver-pwd"
+#define PERSONALISATION_APPLICATION_ID "application-id"
+#define PERSONALIZATION_FBE_KEY "fbe-key"
+
+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/KeyStorage.cpp b/crypto/ext4crypt/KeyStorage.cpp
new file mode 100644
index 000000000..199520e9d
--- /dev/null
+++ b/crypto/ext4crypt/KeyStorage.cpp
@@ -0,0 +1,349 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "KeyStorage.h"
+
+#include "Keymaster.h"
+#include "ScryptParameters.h"
+#include "Utils.h"
+
+#include <vector>
+
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <iostream>
+
+#include <openssl/sha.h>
+
+#include <android-base/file.h>
+//#include <android-base/logging.h>
+
+#include <cutils/properties.h>
+
+#include <hardware/hw_auth_token.h>
+
+#include <keymaster/authorization_set.h>
+
+extern "C" {
+
+#include "crypto_scrypt.h"
+}
+
+#define ERROR 1
+#define LOG(x) std::cout
+#define PLOG(x) std::cout
+
+namespace android {
+namespace vold {
+
+const KeyAuthentication kEmptyAuthentication{"", ""};
+
+static constexpr size_t AES_KEY_BYTES = 32;
+static constexpr size_t GCM_NONCE_BYTES = 12;
+static constexpr size_t GCM_MAC_BYTES = 16;
+static constexpr size_t SALT_BYTES = 1 << 4;
+static constexpr size_t SECDISCARDABLE_BYTES = 1 << 14;
+static constexpr size_t STRETCHED_BYTES = 1 << 6;
+
+static constexpr uint32_t AUTH_TIMEOUT = 30; // Seconds
+
+static const char* kCurrentVersion = "1";
+static const char* kRmPath = "/system/bin/rm";
+static const char* kSecdiscardPath = "/system/bin/secdiscard";
+static const char* kStretch_none = "none";
+static const char* kStretch_nopassword = "nopassword";
+static const std::string kStretchPrefix_scrypt = "scrypt ";
+static const char* kFn_encrypted_key = "encrypted_key";
+static const char* kFn_keymaster_key_blob = "keymaster_key_blob";
+static const char* kFn_salt = "salt";
+static const char* kFn_secdiscardable = "secdiscardable";
+static const char* kFn_stretching = "stretching";
+static const char* kFn_version = "version";
+
+static bool checkSize(const std::string& kind, size_t actual, size_t expected) {
+ if (actual != expected) {
+ LOG(ERROR) << "Wrong number of bytes in " << kind << ", expected " << expected << " got "
+ << actual;
+ return false;
+ }
+ return true;
+}
+
+static std::string hashSecdiscardable(const std::string& secdiscardable) {
+ SHA512_CTX c;
+
+ SHA512_Init(&c);
+ // Personalise the hashing by introducing a fixed prefix.
+ // Hashing applications should use personalization except when there is a
+ // specific reason not to; see section 4.11 of https://www.schneier.com/skein1.3.pdf
+ std::string secdiscardableHashingPrefix = "Android secdiscardable SHA512";
+ secdiscardableHashingPrefix.resize(SHA512_CBLOCK);
+ SHA512_Update(&c, secdiscardableHashingPrefix.data(), secdiscardableHashingPrefix.size());
+ SHA512_Update(&c, secdiscardable.data(), secdiscardable.size());
+ std::string res(SHA512_DIGEST_LENGTH, '\0');
+ SHA512_Final(reinterpret_cast<uint8_t*>(&res[0]), &c);
+ return res;
+}
+
+/*static bool generateKeymasterKey(Keymaster& keymaster, const KeyAuthentication& auth,
+ const std::string& appId, std::string* key) {
+ auto paramBuilder = keymaster::AuthorizationSetBuilder()
+ .AesEncryptionKey(AES_KEY_BYTES * 8)
+ .Authorization(keymaster::TAG_BLOCK_MODE, KM_MODE_GCM)
+ .Authorization(keymaster::TAG_MIN_MAC_LENGTH, GCM_MAC_BYTES * 8)
+ .Authorization(keymaster::TAG_PADDING, KM_PAD_NONE);
+ addStringParam(&paramBuilder, keymaster::TAG_APPLICATION_ID, appId);
+ if (auth.token.empty()) {
+ LOG(DEBUG) << "Creating key that doesn't need auth token";
+ paramBuilder.Authorization(keymaster::TAG_NO_AUTH_REQUIRED);
+ } else {
+ LOG(DEBUG) << "Auth token required for key";
+ if (auth.token.size() != sizeof(hw_auth_token_t)) {
+ LOG(ERROR) << "Auth token should be " << sizeof(hw_auth_token_t) << " bytes, was "
+ << auth.token.size() << " bytes";
+ return false;
+ }
+ const hw_auth_token_t* at = reinterpret_cast<const hw_auth_token_t*>(auth.token.data());
+ paramBuilder.Authorization(keymaster::TAG_USER_SECURE_ID, at->user_id);
+ paramBuilder.Authorization(keymaster::TAG_USER_AUTH_TYPE, HW_AUTH_PASSWORD);
+ paramBuilder.Authorization(keymaster::TAG_AUTH_TIMEOUT, AUTH_TIMEOUT);
+ }
+ return keymaster.generateKey(paramBuilder.build(), key);
+}*/
+
+static keymaster::AuthorizationSetBuilder beginParams(const KeyAuthentication& auth,
+ const std::string& appId) {
+ auto paramBuilder = keymaster::AuthorizationSetBuilder()
+ .Authorization(keymaster::TAG_BLOCK_MODE, KM_MODE_GCM)
+ .Authorization(keymaster::TAG_MAC_LENGTH, GCM_MAC_BYTES * 8)
+ .Authorization(keymaster::TAG_PADDING, KM_PAD_NONE);
+ addStringParam(&paramBuilder, keymaster::TAG_APPLICATION_ID, appId);
+ if (!auth.token.empty()) {
+ LOG(DEBUG) << "Supplying auth token to Keymaster";
+ addStringParam(&paramBuilder, keymaster::TAG_AUTH_TOKEN, auth.token);
+ }
+ return paramBuilder;
+}
+
+/*static bool encryptWithKeymasterKey(Keymaster& keymaster, const std::string& key,
+ const KeyAuthentication& auth, const std::string& appId,
+ const std::string& message, std::string* ciphertext) {
+ auto params = beginParams(auth, appId).build();
+ keymaster::AuthorizationSet outParams;
+ auto opHandle = keymaster.begin(KM_PURPOSE_ENCRYPT, key, params, &outParams);
+ if (!opHandle) return false;
+ keymaster_blob_t nonceBlob;
+ if (!outParams.GetTagValue(keymaster::TAG_NONCE, &nonceBlob)) {
+ LOG(ERROR) << "GCM encryption but no nonce generated";
+ return false;
+ }
+ // nonceBlob here is just a pointer into existing data, must not be freed
+ std::string nonce(reinterpret_cast<const char*>(nonceBlob.data), nonceBlob.data_length);
+ if (!checkSize("nonce", nonce.size(), GCM_NONCE_BYTES)) return false;
+ std::string body;
+ if (!opHandle.updateCompletely(message, &body)) return false;
+
+ std::string mac;
+ if (!opHandle.finishWithOutput(&mac)) return false;
+ if (!checkSize("mac", mac.size(), GCM_MAC_BYTES)) return false;
+ *ciphertext = nonce + body + mac;
+ return true;
+}*/
+
+static bool decryptWithKeymasterKey(Keymaster& keymaster, const std::string& key,
+ const KeyAuthentication& auth, const std::string& appId,
+ const std::string& ciphertext, std::string* message) {
+ auto nonce = ciphertext.substr(0, GCM_NONCE_BYTES);
+ auto bodyAndMac = ciphertext.substr(GCM_NONCE_BYTES);
+ auto params = addStringParam(beginParams(auth, appId), keymaster::TAG_NONCE, nonce).build();
+ auto opHandle = keymaster.begin(KM_PURPOSE_DECRYPT, key, params);
+ if (!opHandle) return false;
+ if (!opHandle.updateCompletely(bodyAndMac, message)) return false;
+ if (!opHandle.finish()) return false;
+ return true;
+}
+
+static bool readFileToString(const std::string& filename, std::string* result) {
+ if (!android::base::ReadFileToString(filename, result)) {
+ PLOG(ERROR) << "Failed to read from " << filename;
+ return false;
+ }
+ return true;
+}
+
+/*static bool writeStringToFile(const std::string& payload, const std::string& filename) {
+ if (!android::base::WriteStringToFile(payload, filename)) {
+ PLOG(ERROR) << "Failed to write to " << filename;
+ return false;
+ }
+ return true;
+}*/
+
+static std::string getStretching() {
+ char paramstr[PROPERTY_VALUE_MAX];
+
+ property_get(SCRYPT_PROP, paramstr, SCRYPT_DEFAULTS);
+ return std::string() + kStretchPrefix_scrypt + paramstr;
+}
+
+static bool stretchingNeedsSalt(const std::string& stretching) {
+ return stretching != kStretch_nopassword && stretching != kStretch_none;
+}
+
+static bool stretchSecret(const std::string& stretching, const std::string& secret,
+ const std::string& salt, std::string* stretched) {
+ if (stretching == kStretch_nopassword) {
+ if (!secret.empty()) {
+ LOG(WARNING) << "Password present but stretching is nopassword";
+ // Continue anyway
+ }
+ stretched->clear();
+ } else if (stretching == kStretch_none) {
+ *stretched = secret;
+ } else if (std::equal(kStretchPrefix_scrypt.begin(), kStretchPrefix_scrypt.end(),
+ stretching.begin())) {
+ int Nf, rf, pf;
+ if (!parse_scrypt_parameters(stretching.substr(kStretchPrefix_scrypt.size()).c_str(), &Nf,
+ &rf, &pf)) {
+ LOG(ERROR) << "Unable to parse scrypt params in stretching: " << stretching;
+ return false;
+ }
+ stretched->assign(STRETCHED_BYTES, '\0');
+ if (crypto_scrypt(reinterpret_cast<const uint8_t*>(secret.data()), secret.size(),
+ reinterpret_cast<const uint8_t*>(salt.data()), salt.size(),
+ 1 << Nf, 1 << rf, 1 << pf,
+ reinterpret_cast<uint8_t*>(&(*stretched)[0]), stretched->size()) != 0) {
+ LOG(ERROR) << "scrypt failed with params: " << stretching;
+ return false;
+ }
+ } else {
+ LOG(ERROR) << "Unknown stretching type: " << stretching;
+ return false;
+ }
+ return true;
+}
+
+static bool generateAppId(const KeyAuthentication& auth, const std::string& stretching,
+ const std::string& salt, const std::string& secdiscardable,
+ std::string* appId) {
+ std::string stretched;
+ if (!stretchSecret(stretching, auth.secret, salt, &stretched)) return false;
+ *appId = hashSecdiscardable(secdiscardable) + stretched;
+ return true;
+}
+
+/*bool storeKey(const std::string& dir, const KeyAuthentication& auth, const std::string& key) {
+ if (TEMP_FAILURE_RETRY(mkdir(dir.c_str(), 0700)) == -1) {
+ PLOG(ERROR) << "key mkdir " << dir;
+ return false;
+ }
+ if (!writeStringToFile(kCurrentVersion, dir + "/" + kFn_version)) return false;
+ std::string secdiscardable;
+ if (ReadRandomBytes(SECDISCARDABLE_BYTES, secdiscardable) != OK) {
+ // TODO status_t plays badly with PLOG, fix it.
+ LOG(ERROR) << "Random read failed";
+ return false;
+ }
+ if (!writeStringToFile(secdiscardable, dir + "/" + kFn_secdiscardable)) return false;
+ std::string stretching = auth.secret.empty() ? kStretch_nopassword : getStretching();
+ if (!writeStringToFile(stretching, dir + "/" + kFn_stretching)) return false;
+ std::string salt;
+ if (stretchingNeedsSalt(stretching)) {
+ if (ReadRandomBytes(SALT_BYTES, salt) != OK) {
+ LOG(ERROR) << "Random read failed";
+ return false;
+ }
+ if (!writeStringToFile(salt, dir + "/" + kFn_salt)) return false;
+ }
+ std::string appId;
+ if (!generateAppId(auth, stretching, salt, secdiscardable, &appId)) return false;
+ Keymaster keymaster;
+ if (!keymaster) return false;
+ std::string kmKey;
+ if (!generateKeymasterKey(keymaster, auth, appId, &kmKey)) return false;
+ if (!writeStringToFile(kmKey, dir + "/" + kFn_keymaster_key_blob)) return false;
+ std::string encryptedKey;
+ if (!encryptWithKeymasterKey(keymaster, kmKey, auth, appId, key, &encryptedKey)) return false;
+ if (!writeStringToFile(encryptedKey, dir + "/" + kFn_encrypted_key)) return false;
+ return true;
+}*/
+
+bool retrieveKey(const std::string& dir, const KeyAuthentication& auth, std::string* key) {
+ std::string version;
+ if (!readFileToString(dir + "/" + kFn_version, &version)) return false;
+ if (version != kCurrentVersion) {
+ LOG(ERROR) << "Version mismatch, expected " << kCurrentVersion << " got " << version;
+ return false;
+ }
+ std::string secdiscardable;
+ if (!readFileToString(dir + "/" + kFn_secdiscardable, &secdiscardable)) return false;
+ std::string stretching;
+ if (!readFileToString(dir + "/" + kFn_stretching, &stretching)) return false;
+ std::string salt;
+ if (stretchingNeedsSalt(stretching)) {
+ if (!readFileToString(dir + "/" + kFn_salt, &salt)) return false;
+ }
+ std::string appId;
+ if (!generateAppId(auth, stretching, salt, secdiscardable, &appId)) return false;
+ std::string kmKey;
+ if (!readFileToString(dir + "/" + kFn_keymaster_key_blob, &kmKey)) return false;
+ std::string encryptedMessage;
+ if (!readFileToString(dir + "/" + kFn_encrypted_key, &encryptedMessage)) return false;
+ Keymaster keymaster;
+ if (!keymaster) return false;
+ return decryptWithKeymasterKey(keymaster, kmKey, auth, appId, encryptedMessage, key);
+}
+
+static bool deleteKey(const std::string& dir) {
+ std::string kmKey;
+ if (!readFileToString(dir + "/" + kFn_keymaster_key_blob, &kmKey)) return false;
+ Keymaster keymaster;
+ if (!keymaster) return false;
+ if (!keymaster.deleteKey(kmKey)) return false;
+ return true;
+}
+
+static bool secdiscardSecdiscardable(const std::string& dir) {
+ if (ForkExecvp(
+ std::vector<std::string>{kSecdiscardPath, "--", dir + "/" + kFn_secdiscardable}) != 0) {
+ LOG(ERROR) << "secdiscard failed";
+ return false;
+ }
+ return true;
+}
+
+static bool recursiveDeleteKey(const std::string& dir) {
+ if (ForkExecvp(std::vector<std::string>{kRmPath, "-rf", dir}) != 0) {
+ LOG(ERROR) << "recursive delete failed";
+ return false;
+ }
+ return true;
+}
+
+bool destroyKey(const std::string& dir) {
+ bool success = true;
+ // Try each thing, even if previous things failed.
+ success &= deleteKey(dir);
+ success &= secdiscardSecdiscardable(dir);
+ success &= recursiveDeleteKey(dir);
+ return success;
+}
+
+} // namespace vold
+} // namespace android
diff --git a/crypto/ext4crypt/KeyStorage.h b/crypto/ext4crypt/KeyStorage.h
new file mode 100644
index 000000000..63d38da25
--- /dev/null
+++ b/crypto/ext4crypt/KeyStorage.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_VOLD_KEYSTORAGE_H
+#define ANDROID_VOLD_KEYSTORAGE_H
+
+#include <string>
+
+namespace android {
+namespace vold {
+
+// Represents the information needed to decrypt a disk encryption key.
+// If "token" is nonempty, it is passed in as a required Gatekeeper auth token.
+// If "secret" is nonempty, it is appended to the application-specific
+// binary needed to unlock.
+class KeyAuthentication {
+ public:
+ KeyAuthentication(std::string t, std::string s) : token{t}, secret{s} {};
+ const std::string token;
+ const std::string secret;
+};
+
+extern const KeyAuthentication kEmptyAuthentication;
+
+// Create a directory at the named path, and store "key" in it,
+// in such a way that it can only be retrieved via Keymaster and
+// can be securely deleted.
+// It's safe to move/rename the directory after creation.
+//bool storeKey(const std::string& dir, const KeyAuthentication& auth, const std::string& key);
+
+// Retrieve the key from the named directory.
+bool retrieveKey(const std::string& dir, const KeyAuthentication& auth, std::string* key);
+
+// Securely destroy the key stored in the named directory and delete the directory.
+bool destroyKey(const std::string& dir);
+
+} // namespace vold
+} // namespace android
+
+#endif
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/Keymaster.cpp b/crypto/ext4crypt/Keymaster.cpp
new file mode 100644
index 000000000..3c21aa26d
--- /dev/null
+++ b/crypto/ext4crypt/Keymaster.cpp
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Keymaster.h"
+
+//#include <android-base/logging.h>
+#include <hardware/hardware.h>
+#include <hardware/keymaster1.h>
+#include <hardware/keymaster2.h>
+
+#include <iostream>
+#define ERROR 1
+#define LOG(x) std::cout
+
+namespace android {
+namespace vold {
+
+class IKeymasterDevice {
+ public:
+ IKeymasterDevice() {}
+ virtual ~IKeymasterDevice() {}
+ /*virtual keymaster_error_t generate_key(const keymaster_key_param_set_t* params,
+ keymaster_key_blob_t* key_blob) const = 0;*/
+ virtual keymaster_error_t delete_key(const keymaster_key_blob_t* key) const = 0;
+ virtual keymaster_error_t begin(keymaster_purpose_t purpose, const keymaster_key_blob_t* key,
+ const keymaster_key_param_set_t* in_params,
+ keymaster_key_param_set_t* out_params,
+ keymaster_operation_handle_t* operation_handle) const = 0;
+ virtual keymaster_error_t update(keymaster_operation_handle_t operation_handle,
+ const keymaster_key_param_set_t* in_params,
+ const keymaster_blob_t* input, size_t* input_consumed,
+ keymaster_key_param_set_t* out_params,
+ keymaster_blob_t* output) const = 0;
+ virtual keymaster_error_t finish(keymaster_operation_handle_t operation_handle,
+ const keymaster_key_param_set_t* in_params,
+ const keymaster_blob_t* signature,
+ keymaster_key_param_set_t* out_params,
+ keymaster_blob_t* output) const = 0;
+ virtual keymaster_error_t abort(keymaster_operation_handle_t operation_handle) const = 0;
+
+ protected:
+ DISALLOW_COPY_AND_ASSIGN(IKeymasterDevice);
+};
+
+template <typename T> class KeymasterDevice : public IKeymasterDevice {
+ public:
+ KeymasterDevice(T* d) : mDevice{d} {}
+ /*keymaster_error_t generate_key(const keymaster_key_param_set_t* params,
+ keymaster_key_blob_t* key_blob) const override final {
+ return mDevice->generate_key(mDevice, params, key_blob, nullptr);
+ }*/
+ keymaster_error_t delete_key(const keymaster_key_blob_t* key) const override final {
+ if (mDevice->delete_key == nullptr) return KM_ERROR_OK;
+ return mDevice->delete_key(mDevice, key);
+ }
+ keymaster_error_t begin(keymaster_purpose_t purpose, const keymaster_key_blob_t* key,
+ const keymaster_key_param_set_t* in_params,
+ keymaster_key_param_set_t* out_params,
+ keymaster_operation_handle_t* operation_handle) const override final {
+ return mDevice->begin(mDevice, purpose, key, in_params, out_params, operation_handle);
+ }
+ keymaster_error_t update(keymaster_operation_handle_t operation_handle,
+ const keymaster_key_param_set_t* in_params,
+ const keymaster_blob_t* input, size_t* input_consumed,
+ keymaster_key_param_set_t* out_params,
+ keymaster_blob_t* output) const override final {
+ return mDevice->update(mDevice, operation_handle, in_params, input, input_consumed,
+ out_params, output);
+ }
+ keymaster_error_t abort(keymaster_operation_handle_t operation_handle) const override final {
+ return mDevice->abort(mDevice, operation_handle);
+ }
+
+ protected:
+ T* const mDevice;
+};
+
+class Keymaster1Device : public KeymasterDevice<keymaster1_device_t> {
+ public:
+ Keymaster1Device(keymaster1_device_t* d) : KeymasterDevice<keymaster1_device_t>{d} {}
+ ~Keymaster1Device() override final { keymaster1_close(mDevice); }
+ keymaster_error_t finish(keymaster_operation_handle_t operation_handle,
+ const keymaster_key_param_set_t* in_params,
+ const keymaster_blob_t* signature,
+ keymaster_key_param_set_t* out_params,
+ keymaster_blob_t* output) const override final {
+ return mDevice->finish(mDevice, operation_handle, in_params, signature, out_params, output);
+ }
+};
+
+class Keymaster2Device : public KeymasterDevice<keymaster2_device_t> {
+ public:
+ Keymaster2Device(keymaster2_device_t* d) : KeymasterDevice<keymaster2_device_t>{d} {}
+ ~Keymaster2Device() override final { keymaster2_close(mDevice); }
+ keymaster_error_t finish(keymaster_operation_handle_t operation_handle,
+ const keymaster_key_param_set_t* in_params,
+ const keymaster_blob_t* signature,
+ keymaster_key_param_set_t* out_params,
+ keymaster_blob_t* output) const override final {
+ return mDevice->finish(mDevice, operation_handle, in_params, nullptr, signature, out_params,
+ output);
+ }
+};
+
+KeymasterOperation::~KeymasterOperation() {
+ if (mDevice) mDevice->abort(mOpHandle);
+}
+
+bool KeymasterOperation::updateCompletely(const std::string& input, std::string* output) {
+ output->clear();
+ auto it = input.begin();
+ while (it != input.end()) {
+ size_t toRead = static_cast<size_t>(input.end() - it);
+ keymaster_blob_t inputBlob{reinterpret_cast<const uint8_t*>(&*it), toRead};
+ keymaster_blob_t outputBlob;
+ size_t inputConsumed;
+ auto error =
+ mDevice->update(mOpHandle, nullptr, &inputBlob, &inputConsumed, nullptr, &outputBlob);
+ if (error != KM_ERROR_OK) {
+ LOG(ERROR) << "update failed, code " << error;
+ mDevice = nullptr;
+ return false;
+ }
+ output->append(reinterpret_cast<const char*>(outputBlob.data), outputBlob.data_length);
+ free(const_cast<uint8_t*>(outputBlob.data));
+ if (inputConsumed > toRead) {
+ LOG(ERROR) << "update reported too much input consumed";
+ mDevice = nullptr;
+ return false;
+ }
+ it += inputConsumed;
+ }
+ return true;
+}
+
+bool KeymasterOperation::finish() {
+ auto error = mDevice->finish(mOpHandle, nullptr, nullptr, nullptr, nullptr);
+ mDevice = nullptr;
+ if (error != KM_ERROR_OK) {
+ LOG(ERROR) << "finish failed, code " << error;
+ return false;
+ }
+ return true;
+}
+
+bool KeymasterOperation::finishWithOutput(std::string* output) {
+ keymaster_blob_t outputBlob;
+ auto error = mDevice->finish(mOpHandle, nullptr, nullptr, nullptr, &outputBlob);
+ mDevice = nullptr;
+ if (error != KM_ERROR_OK) {
+ LOG(ERROR) << "finish failed, code " << error;
+ return false;
+ }
+ output->assign(reinterpret_cast<const char*>(outputBlob.data), outputBlob.data_length);
+ free(const_cast<uint8_t*>(outputBlob.data));
+ return true;
+}
+
+Keymaster::Keymaster() {
+ mDevice = nullptr;
+ const hw_module_t* module;
+ int ret = hw_get_module_by_class(KEYSTORE_HARDWARE_MODULE_ID, NULL, &module);
+ if (ret != 0) {
+ LOG(ERROR) << "hw_get_module_by_class returned " << ret;
+ return;
+ }
+ if (module->module_api_version == KEYMASTER_MODULE_API_VERSION_1_0) {
+ keymaster1_device_t* device;
+ ret = keymaster1_open(module, &device);
+ if (ret != 0) {
+ LOG(ERROR) << "keymaster1_open returned " << ret;
+ return;
+ }
+ mDevice = std::make_shared<Keymaster1Device>(device);
+ } else if (module->module_api_version == KEYMASTER_MODULE_API_VERSION_2_0) {
+ keymaster2_device_t* device;
+ ret = keymaster2_open(module, &device);
+ if (ret != 0) {
+ LOG(ERROR) << "keymaster2_open returned " << ret;
+ return;
+ }
+ mDevice = std::make_shared<Keymaster2Device>(device);
+ } else {
+ LOG(ERROR) << "module_api_version is " << module->module_api_version;
+ return;
+ }
+}
+
+/*bool Keymaster::generateKey(const keymaster::AuthorizationSet& inParams, std::string* key) {
+ keymaster_key_blob_t keyBlob;
+ auto error = mDevice->generate_key(&inParams, &keyBlob);
+ if (error != KM_ERROR_OK) {
+ LOG(ERROR) << "generate_key failed, code " << error;
+ return false;
+ }
+ key->assign(reinterpret_cast<const char*>(keyBlob.key_material), keyBlob.key_material_size);
+ free(const_cast<uint8_t*>(keyBlob.key_material));
+ return true;
+}*/
+
+bool Keymaster::deleteKey(const std::string& key) {
+ keymaster_key_blob_t keyBlob{reinterpret_cast<const uint8_t*>(key.data()), key.size()};
+ auto error = mDevice->delete_key(&keyBlob);
+ if (error != KM_ERROR_OK) {
+ LOG(ERROR) << "delete_key failed, code " << error;
+ return false;
+ }
+ return true;
+}
+
+KeymasterOperation Keymaster::begin(keymaster_purpose_t purpose, const std::string& key,
+ const keymaster::AuthorizationSet& inParams,
+ keymaster::AuthorizationSet* outParams) {
+ keymaster_key_blob_t keyBlob{reinterpret_cast<const uint8_t*>(key.data()), key.size()};
+ keymaster_operation_handle_t mOpHandle;
+ keymaster_key_param_set_t outParams_set;
+ auto error = mDevice->begin(purpose, &keyBlob, &inParams, &outParams_set, &mOpHandle);
+ if (error != KM_ERROR_OK) {
+ LOG(ERROR) << "begin failed, code " << error;
+ return KeymasterOperation(nullptr, mOpHandle);
+ }
+ outParams->Clear();
+ outParams->push_back(outParams_set);
+ keymaster_free_param_set(&outParams_set);
+ return KeymasterOperation(mDevice, mOpHandle);
+}
+
+KeymasterOperation Keymaster::begin(keymaster_purpose_t purpose, const std::string& key,
+ const keymaster::AuthorizationSet& inParams) {
+ keymaster_key_blob_t keyBlob{reinterpret_cast<const uint8_t*>(key.data()), key.size()};
+ keymaster_operation_handle_t mOpHandle;
+ auto error = mDevice->begin(purpose, &keyBlob, &inParams, nullptr, &mOpHandle);
+ if (error != KM_ERROR_OK) {
+ LOG(ERROR) << "begin failed, code " << error;
+ return KeymasterOperation(nullptr, mOpHandle);
+ }
+ return KeymasterOperation(mDevice, mOpHandle);
+}
+
+} // namespace vold
+} // namespace android
diff --git a/crypto/ext4crypt/Keymaster.h b/crypto/ext4crypt/Keymaster.h
new file mode 100644
index 000000000..11b3532ad
--- /dev/null
+++ b/crypto/ext4crypt/Keymaster.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_VOLD_KEYMASTER_H
+#define ANDROID_VOLD_KEYMASTER_H
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include <keymaster/authorization_set.h>
+
+namespace android {
+namespace vold {
+
+using namespace keymaster;
+
+// C++ wrappers to the Keymaster C interface.
+// This is tailored to the needs of KeyStorage, but could be extended to be
+// a more general interface.
+
+// Class that wraps a keymaster1_device_t or keymaster2_device_t and provides methods
+// they have in common. Also closes the device on destruction.
+class IKeymasterDevice;
+
+// Wrapper for a keymaster_operation_handle_t representing an
+// ongoing Keymaster operation. Aborts the operation
+// in the destructor if it is unfinished. Methods log failures
+// to LOG(ERROR).
+class KeymasterOperation {
+ public:
+ ~KeymasterOperation();
+ // Is this instance valid? This is false if creation fails, and becomes
+ // false on finish or if an update fails.
+ explicit operator bool() { return mDevice != nullptr; }
+ // Call "update" repeatedly until all of the input is consumed, and
+ // concatenate the output. Return true on success.
+ bool updateCompletely(const std::string& input, std::string* output);
+ // Finish; pass nullptr for the "output" param.
+ bool finish();
+ // Finish and write the output to this string.
+ bool finishWithOutput(std::string* output);
+ // Move constructor
+ KeymasterOperation(KeymasterOperation&& rhs) {
+ mOpHandle = std::move(rhs.mOpHandle);
+ mDevice = std::move(rhs.mDevice);
+ }
+
+ private:
+ KeymasterOperation(std::shared_ptr<IKeymasterDevice> d, keymaster_operation_handle_t h)
+ : mDevice{d}, mOpHandle{h} {}
+ std::shared_ptr<IKeymasterDevice> mDevice;
+ keymaster_operation_handle_t mOpHandle;
+ DISALLOW_COPY_AND_ASSIGN(KeymasterOperation);
+ friend class Keymaster;
+};
+
+// Wrapper for a Keymaster device for methods that start a KeymasterOperation or are not
+// part of one.
+class Keymaster {
+ public:
+ Keymaster();
+ // false if we failed to open the keymaster device.
+ explicit operator bool() { return mDevice != nullptr; }
+ // Generate a key in the keymaster from the given params.
+ //bool generateKey(const AuthorizationSet& inParams, std::string* key);
+ // If the keymaster supports it, permanently delete a key.
+ bool deleteKey(const std::string& key);
+ // Begin a new cryptographic operation, collecting output parameters.
+ KeymasterOperation begin(keymaster_purpose_t purpose, const std::string& key,
+ const AuthorizationSet& inParams, AuthorizationSet* outParams);
+ // Begin a new cryptographic operation; don't collect output parameters.
+ KeymasterOperation begin(keymaster_purpose_t purpose, const std::string& key,
+ const AuthorizationSet& inParams);
+
+ private:
+ std::shared_ptr<IKeymasterDevice> mDevice;
+ DISALLOW_COPY_AND_ASSIGN(Keymaster);
+};
+
+template <keymaster_tag_t Tag>
+inline AuthorizationSetBuilder& addStringParam(AuthorizationSetBuilder&& params,
+ TypedTag<KM_BYTES, Tag> tag,
+ const std::string& val) {
+ return params.Authorization(tag, val.data(), val.size());
+}
+
+template <keymaster_tag_t Tag>
+inline void addStringParam(AuthorizationSetBuilder* params, TypedTag<KM_BYTES, Tag> tag,
+ const std::string& val) {
+ params->Authorization(tag, val.data(), val.size());
+}
+
+} // namespace vold
+} // namespace android
+
+#endif
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/ScryptParameters.cpp b/crypto/ext4crypt/ScryptParameters.cpp
new file mode 100644
index 000000000..669809b9f
--- /dev/null
+++ b/crypto/ext4crypt/ScryptParameters.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "ScryptParameters.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+bool parse_scrypt_parameters(const char* paramstr, int *Nf, int *rf, int *pf) {
+ int params[3];
+ char *token;
+ char *saveptr;
+ int i;
+
+ /*
+ * The token we're looking for should be three integers separated by
+ * colons (e.g., "12:8:1"). Scan the property to make sure it matches.
+ */
+ for (i = 0, token = strtok_r(const_cast<char *>(paramstr), ":", &saveptr);
+ token != nullptr && i < 3;
+ i++, token = strtok_r(nullptr, ":", &saveptr)) {
+ char *endptr;
+ params[i] = strtol(token, &endptr, 10);
+
+ /*
+ * Check that there was a valid number and it's 8-bit.
+ */
+ if ((*token == '\0') || (*endptr != '\0') || params[i] < 0 || params[i] > 255) {
+ return false;
+ }
+ }
+ if (token != nullptr) {
+ return false;
+ }
+ *Nf = params[0]; *rf = params[1]; *pf = params[2];
+ return true;
+}
diff --git a/crypto/ext4crypt/ScryptParameters.h b/crypto/ext4crypt/ScryptParameters.h
new file mode 100644
index 000000000..1b43ea574
--- /dev/null
+++ b/crypto/ext4crypt/ScryptParameters.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_VOLD_SCRYPT_PARAMETERS_H
+#define ANDROID_VOLD_SCRYPT_PARAMETERS_H
+
+#include <stdbool.h>
+#include <sys/cdefs.h>
+
+#define SCRYPT_PROP "ro.crypto.scrypt_params"
+#define SCRYPT_DEFAULTS "15:3:1"
+
+__BEGIN_DECLS
+
+bool parse_scrypt_parameters(const char* paramstr, int *Nf, int *rf, int *pf);
+
+__END_DECLS
+
+#endif
diff --git a/crypto/ext4crypt/Utils.cpp b/crypto/ext4crypt/Utils.cpp
new file mode 100644
index 000000000..f0bf029b9
--- /dev/null
+++ b/crypto/ext4crypt/Utils.cpp
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Utils.h"
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+
+#include <fcntl.h>
+#include <linux/fs.h>
+#include <stdlib.h>
+#include <sys/mount.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/statvfs.h>
+
+#include <selinux/android.h>
+
+using android::base::ReadFileToString;
+using android::base::StringPrintf;
+
+namespace android {
+namespace vold {
+
+static const char* kKeyPath = "/data/misc/vold";
+
+status_t ForkExecvp(const std::vector<std::string>& args) {
+ return ForkExecvp(args, nullptr);
+}
+
+status_t ForkExecvp(const std::vector<std::string>& args, security_context_t context) {
+ size_t argc = args.size();
+ char** argv = (char**) calloc(argc, sizeof(char*));
+ for (size_t i = 0; i < argc; i++) {
+ argv[i] = (char*) args[i].c_str();
+ if (i == 0) {
+ LOG(VERBOSE) << args[i];
+ } else {
+ LOG(VERBOSE) << " " << args[i];
+ }
+ }
+
+ if (setexeccon(context)) {
+ LOG(ERROR) << "Failed to setexeccon";
+ abort();
+ }
+ abort();
+ status_t res = 1;//android_fork_execvp(argc, argv, NULL, false, true);
+ if (setexeccon(nullptr)) {
+ LOG(ERROR) << "Failed to setexeccon";
+ abort();
+ }
+
+ free(argv);
+ return res;
+}
+
+status_t ForkExecvp(const std::vector<std::string>& args,
+ std::vector<std::string>& output) {
+ return ForkExecvp(args, output, nullptr);
+}
+
+status_t ForkExecvp(const std::vector<std::string>& args,
+ std::vector<std::string>& output, security_context_t context) {
+ std::string cmd;
+ for (size_t i = 0; i < args.size(); i++) {
+ cmd += args[i] + " ";
+ if (i == 0) {
+ LOG(VERBOSE) << args[i];
+ } else {
+ LOG(VERBOSE) << " " << args[i];
+ }
+ }
+ output.clear();
+
+ if (setexeccon(context)) {
+ LOG(ERROR) << "Failed to setexeccon";
+ abort();
+ }
+ FILE* fp = popen(cmd.c_str(), "r");
+ if (setexeccon(nullptr)) {
+ LOG(ERROR) << "Failed to setexeccon";
+ abort();
+ }
+
+ if (!fp) {
+ PLOG(ERROR) << "Failed to popen " << cmd;
+ return -errno;
+ }
+ char line[1024];
+ while (fgets(line, sizeof(line), fp) != nullptr) {
+ LOG(VERBOSE) << line;
+ output.push_back(std::string(line));
+ }
+ if (pclose(fp) != 0) {
+ PLOG(ERROR) << "Failed to pclose " << cmd;
+ return -errno;
+ }
+
+ return OK;
+}
+
+pid_t ForkExecvpAsync(const std::vector<std::string>& args) {
+ size_t argc = args.size();
+ char** argv = (char**) calloc(argc + 1, sizeof(char*));
+ for (size_t i = 0; i < argc; i++) {
+ argv[i] = (char*) args[i].c_str();
+ if (i == 0) {
+ LOG(VERBOSE) << args[i];
+ } else {
+ LOG(VERBOSE) << " " << args[i];
+ }
+ }
+
+ pid_t pid = fork();
+ if (pid == 0) {
+ close(STDIN_FILENO);
+ close(STDOUT_FILENO);
+ close(STDERR_FILENO);
+
+ if (execvp(argv[0], argv)) {
+ PLOG(ERROR) << "Failed to exec";
+ }
+
+ _exit(1);
+ }
+
+ if (pid == -1) {
+ PLOG(ERROR) << "Failed to exec";
+ }
+
+ free(argv);
+ return pid;
+}
+
+status_t ReadRandomBytes(size_t bytes, std::string& out) {
+ out.clear();
+
+ int fd = TEMP_FAILURE_RETRY(open("/dev/urandom", O_RDONLY | O_CLOEXEC | O_NOFOLLOW));
+ if (fd == -1) {
+ return -errno;
+ }
+
+ char buf[BUFSIZ];
+ size_t n;
+ while ((n = TEMP_FAILURE_RETRY(read(fd, &buf[0], std::min(sizeof(buf), bytes)))) > 0) {
+ out.append(buf, n);
+ bytes -= n;
+ }
+ close(fd);
+
+ if (bytes == 0) {
+ return OK;
+ } else {
+ return -EIO;
+ }
+}
+
+status_t HexToStr(const std::string& hex, std::string& str) {
+ str.clear();
+ bool even = true;
+ char cur = 0;
+ for (size_t i = 0; i < hex.size(); i++) {
+ int val = 0;
+ switch (hex[i]) {
+ case ' ': case '-': case ':': continue;
+ case 'f': case 'F': val = 15; break;
+ case 'e': case 'E': val = 14; break;
+ case 'd': case 'D': val = 13; break;
+ case 'c': case 'C': val = 12; break;
+ case 'b': case 'B': val = 11; break;
+ case 'a': case 'A': val = 10; break;
+ case '9': val = 9; break;
+ case '8': val = 8; break;
+ case '7': val = 7; break;
+ case '6': val = 6; break;
+ case '5': val = 5; break;
+ case '4': val = 4; break;
+ case '3': val = 3; break;
+ case '2': val = 2; break;
+ case '1': val = 1; break;
+ case '0': val = 0; break;
+ default: return -EINVAL;
+ }
+
+ if (even) {
+ cur = val << 4;
+ } else {
+ cur += val;
+ str.push_back(cur);
+ cur = 0;
+ }
+ even = !even;
+ }
+ return even ? OK : -EINVAL;
+}
+
+static bool isValidFilename(const std::string& name) {
+ if (name.empty() || (name == ".") || (name == "..")
+ || (name.find('/') != std::string::npos)) {
+ return false;
+ } else {
+ return true;
+ }
+}
+
+std::string BuildKeyPath(const std::string& partGuid) {
+ return StringPrintf("%s/expand_%s.key", kKeyPath, partGuid.c_str());
+}
+
+std::string BuildDataSystemLegacyPath(userid_t userId) {
+ return StringPrintf("%s/system/users/%u", BuildDataPath(nullptr).c_str(), userId);
+}
+
+std::string BuildDataSystemCePath(userid_t userId) {
+ return StringPrintf("%s/system_ce/%u", BuildDataPath(nullptr).c_str(), userId);
+}
+
+std::string BuildDataSystemDePath(userid_t userId) {
+ return StringPrintf("%s/system_de/%u", BuildDataPath(nullptr).c_str(), userId);
+}
+
+std::string BuildDataMiscLegacyPath(userid_t userId) {
+ return StringPrintf("%s/misc/user/%u", BuildDataPath(nullptr).c_str(), userId);
+}
+
+std::string BuildDataMiscCePath(userid_t userId) {
+ return StringPrintf("%s/misc_ce/%u", BuildDataPath(nullptr).c_str(), userId);
+}
+
+std::string BuildDataMiscDePath(userid_t userId) {
+ return StringPrintf("%s/misc_de/%u", BuildDataPath(nullptr).c_str(), userId);
+}
+
+// Keep in sync with installd (frameworks/native/cmds/installd/utils.h)
+std::string BuildDataProfilesDePath(userid_t userId) {
+ return StringPrintf("%s/misc/profiles/cur/%u", BuildDataPath(nullptr).c_str(), userId);
+}
+
+std::string BuildDataProfilesForeignDexDePath(userid_t userId) {
+ std::string profiles_path = BuildDataProfilesDePath(userId);
+ return StringPrintf("%s/foreign-dex", profiles_path.c_str());
+}
+
+std::string BuildDataPath(const char* volumeUuid) {
+ // TODO: unify with installd path generation logic
+ if (volumeUuid == nullptr) {
+ return "/data";
+ } else {
+ CHECK(isValidFilename(volumeUuid));
+ return StringPrintf("/mnt/expand/%s", volumeUuid);
+ }
+}
+
+std::string BuildDataMediaCePath(const char* volumeUuid, userid_t userId) {
+ // TODO: unify with installd path generation logic
+ std::string data(BuildDataPath(volumeUuid));
+ return StringPrintf("%s/media/%u", data.c_str(), userId);
+}
+
+std::string BuildDataUserCePath(const char* volumeUuid, userid_t userId) {
+ // TODO: unify with installd path generation logic
+ std::string data(BuildDataPath(volumeUuid));
+ if (volumeUuid == nullptr) {
+ if (userId == 0) {
+ return StringPrintf("%s/data", data.c_str());
+ } else {
+ return StringPrintf("%s/user/%u", data.c_str(), userId);
+ }
+ } else {
+ return StringPrintf("%s/user/%u", data.c_str(), userId);
+ }
+}
+
+std::string BuildDataUserDePath(const char* volumeUuid, userid_t userId) {
+ // TODO: unify with installd path generation logic
+ std::string data(BuildDataPath(volumeUuid));
+ return StringPrintf("%s/user_de/%u", data.c_str(), userId);
+}
+
+} // namespace vold
+} // namespace android
diff --git a/crypto/ext4crypt/Utils.h b/crypto/ext4crypt/Utils.h
new file mode 100644
index 000000000..aede20341
--- /dev/null
+++ b/crypto/ext4crypt/Utils.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TWRP_VOLD_UTILS_H
+#define TWRP_VOLD_UTILS_H
+
+#include <utils/Errors.h>
+#include <cutils/multiuser.h>
+#include <selinux/selinux.h>
+
+#include <vector>
+#include <string>
+
+// DISALLOW_COPY_AND_ASSIGN disallows the copy and operator= functions. It goes in the private:
+// declarations in a class.
+#if !defined(DISALLOW_COPY_AND_ASSIGN)
+#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
+ TypeName(const TypeName&) = delete; \
+ void operator=(const TypeName&) = delete
+#endif
+
+namespace android {
+namespace vold {
+
+/* Returns either WEXITSTATUS() status, or a negative errno */
+status_t ForkExecvp(const std::vector<std::string>& args);
+status_t ForkExecvp(const std::vector<std::string>& args, security_context_t context);
+
+status_t ForkExecvp(const std::vector<std::string>& args,
+ std::vector<std::string>& output);
+status_t ForkExecvp(const std::vector<std::string>& args,
+ std::vector<std::string>& output, security_context_t context);
+
+pid_t ForkExecvpAsync(const std::vector<std::string>& args);
+
+status_t ReadRandomBytes(size_t bytes, std::string& out);
+
+/* Converts hex string to raw bytes, ignoring [ :-] */
+status_t HexToStr(const std::string& hex, std::string& str);
+
+std::string BuildKeyPath(const std::string& partGuid);
+
+std::string BuildDataSystemLegacyPath(userid_t userid);
+std::string BuildDataSystemCePath(userid_t userid);
+std::string BuildDataSystemDePath(userid_t userid);
+std::string BuildDataMiscLegacyPath(userid_t userid);
+std::string BuildDataMiscCePath(userid_t userid);
+std::string BuildDataMiscDePath(userid_t userid);
+std::string BuildDataProfilesDePath(userid_t userid);
+std::string BuildDataProfilesForeignDexDePath(userid_t userid);
+
+std::string BuildDataPath(const char* volumeUuid);
+std::string BuildDataMediaCePath(const char* volumeUuid, userid_t userid);
+std::string BuildDataUserCePath(const char* volumeUuid, userid_t userid);
+std::string BuildDataUserDePath(const char* volumeUuid, userid_t userid);
+
+} // namespace vold
+} // namespace android
+
+#endif
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
new file mode 100644
index 000000000..05de86fe7
--- /dev/null
+++ b/crypto/ext4crypt/e4policyget.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2016 Team Win Recovery Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "ext4crypt_tar.h"
+
+#define EXT4_KEY_DESCRIPTOR_SIZE 8
+#define EXT4_KEY_DESCRIPTOR_SIZE_HEX 17
+
+int main(int argc, char *argv[]) {
+ bool ret = false;
+ if (argc != 2) {
+ printf("Must specify a path\n");
+ return -1;
+ } else {
+ ext4_encryption_policy eep;
+ if (e4crypt_policy_get_struct(argv[1], &eep, sizeof(eep))) {
+ char policy_hex[EXT4_KEY_DESCRIPTOR_SIZE_HEX];
+ policy_to_hex(eep.master_key_descriptor, policy_hex);
+ printf("%s\n", policy_hex);
+ } else {
+ printf("No policy set\n");
+ }
+ }
+ return 0;
+}
diff --git a/crypto/ext4crypt/ext4_crypt.cpp b/crypto/ext4crypt/ext4_crypt.cpp
new file mode 100644
index 000000000..5a3b4b20c
--- /dev/null
+++ b/crypto/ext4crypt/ext4_crypt.cpp
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* TWRP NOTE: Kanged from system/extras/ext4_utils/ext4_crypt.cpp
+ * because policy_to_hex, e4crypt_policy_set, and e4crypt_policy_get
+ * are not exposed to be used. There was also a bug in e4crypt_policy_get
+ * that may or may not be fixed in the user's local repo:
+ * https://android.googlesource.com/platform/system/extras/+/30b93dd5715abcabd621235733733c0503f9c552
+ */
+
+#include "ext4_crypt.h"
+#include "ext4crypt_tar.h"
+
+#include <dirent.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <fcntl.h>
+#include <asm/ioctl.h>
+#include <sys/syscall.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <cutils/properties.h>
+
+#define XATTR_NAME_ENCRYPTION_POLICY "encryption.policy"
+#define EXT4_KEYREF_DELIMITER ((char)'.')
+
+#define EXT4_ENCRYPTION_MODE_AES_256_XTS 1
+#define EXT4_ENCRYPTION_MODE_AES_256_CTS 4
+#define EXT4_ENCRYPTION_MODE_AES_256_HEH 126
+#define EXT4_ENCRYPTION_MODE_PRIVATE 127
+
+static int encryption_mode = EXT4_ENCRYPTION_MODE_PRIVATE;
+
+#define HEX_LOOKUP "0123456789abcdef"
+
+extern "C" void policy_to_hex(const char* policy, char* hex) {
+ for (size_t i = 0, j = 0; i < EXT4_KEY_DESCRIPTOR_SIZE; i++) {
+ hex[j++] = HEX_LOOKUP[(policy[i] & 0xF0) >> 4];
+ hex[j++] = HEX_LOOKUP[policy[i] & 0x0F];
+ }
+ hex[EXT4_KEY_DESCRIPTOR_SIZE_HEX - 1] = '\0';
+}
+
+extern "C" bool e4crypt_policy_set(const char *directory, const char *policy,
+ size_t policy_length, int contents_encryption_mode) {
+ if (contents_encryption_mode == 0)
+ contents_encryption_mode = encryption_mode;
+ if (policy_length != EXT4_KEY_DESCRIPTOR_SIZE) {
+ printf("policy wrong length\n");
+ LOG(ERROR) << "Policy wrong length: " << policy_length;
+ return false;
+ }
+ int fd = open(directory, O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC);
+ if (fd == -1) {
+ printf("failed to open %s\n", directory);
+ PLOG(ERROR) << "Failed to open directory " << directory;
+ return false;
+ }
+
+ ext4_encryption_policy eep;
+ eep.version = 0;
+ eep.contents_encryption_mode = contents_encryption_mode;
+ eep.filenames_encryption_mode = EXT4_ENCRYPTION_MODE_AES_256_CTS;
+ eep.flags = 0;
+ memcpy(eep.master_key_descriptor, policy, EXT4_KEY_DESCRIPTOR_SIZE);
+ if (ioctl(fd, EXT4_IOC_SET_ENCRYPTION_POLICY, &eep)) {
+ printf("failed to set policy for '%s' '%s'\n", directory, policy);
+ PLOG(ERROR) << "Failed to set encryption policy for " << directory;
+ close(fd);
+ return false;
+ }
+ close(fd);
+
+ char policy_hex[EXT4_KEY_DESCRIPTOR_SIZE_HEX];
+ policy_to_hex(policy, policy_hex);
+ LOG(INFO) << "Policy for " << directory << " set to " << policy_hex;
+ return true;
+}
+
+extern "C" bool e4crypt_policy_get(const char *directory, char *policy,
+ size_t policy_length, int contents_encryption_mode) {
+ if (contents_encryption_mode == 0)
+ contents_encryption_mode = encryption_mode;
+ if (policy_length != EXT4_KEY_DESCRIPTOR_SIZE) {
+ LOG(ERROR) << "Policy wrong length: " << policy_length;
+ return false;
+ }
+
+ int fd = open(directory, O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC);
+ if (fd == -1) {
+ PLOG(ERROR) << "Failed to open directory " << directory;
+ return false;
+ }
+
+ ext4_encryption_policy eep;
+ memset(&eep, 0, sizeof(ext4_encryption_policy));
+ if (ioctl(fd, EXT4_IOC_GET_ENCRYPTION_POLICY, &eep) != 0) {
+ PLOG(ERROR) << "Failed to get encryption policy for " << directory;
+ close(fd);
+ return false;
+ }
+ close(fd);
+
+ if ((eep.version != 0)
+ || (eep.contents_encryption_mode != contents_encryption_mode)
+ || (eep.filenames_encryption_mode != EXT4_ENCRYPTION_MODE_AES_256_CTS)
+ || (eep.flags != 0)) {
+ LOG(ERROR) << "Failed to find matching encryption policy for " << directory;
+ return false;
+ }
+ memcpy(policy, eep.master_key_descriptor, EXT4_KEY_DESCRIPTOR_SIZE);
+
+ return true;
+}
+
+extern "C" void e4crypt_policy_fill_default_struct(ext4_encryption_policy *eep) {
+ eep->version = 0;
+ eep->contents_encryption_mode = encryption_mode;
+ eep->filenames_encryption_mode = EXT4_ENCRYPTION_MODE_AES_256_CTS;
+ eep->flags = 0;
+ memset((void*)&eep->master_key_descriptor[0], 0, EXT4_KEY_DESCRIPTOR_SIZE);
+}
+
+extern "C" bool e4crypt_policy_set_struct(const char *directory, const ext4_encryption_policy *eep) {
+ int fd = open(directory, O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC);
+ if (fd == -1) {
+ printf("failed to open %s\n", directory);
+ PLOG(ERROR) << "Failed to open directory " << directory;
+ return false;
+ }
+ if (ioctl(fd, EXT4_IOC_SET_ENCRYPTION_POLICY, eep)) {
+ printf("failed to set policy for '%s'\n", directory);
+ PLOG(ERROR) << "Failed to set encryption policy for " << directory;
+ close(fd);
+ return false;
+ }
+ close(fd);
+ return true;
+}
+
+extern "C" bool e4crypt_policy_get_struct(const char *directory, ext4_encryption_policy *eep) {
+ int fd = open(directory, O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC);
+ if (fd == -1) {
+ printf("Failed to open '%s'\n", directory);
+ PLOG(ERROR) << "Failed to open directory " << directory;
+ return false;
+ }
+ memset(eep, 0, sizeof(ext4_encryption_policy));
+ if (ioctl(fd, EXT4_IOC_GET_ENCRYPTION_POLICY, eep) != 0) {
+ PLOG(ERROR) << "Failed to get encryption policy for " << directory;
+ close(fd);
+ return false;
+ }
+ close(fd);
+ return true;
+}
+
+extern "C" bool e4crypt_set_mode() {
+ const char* mode_file = "/data/unencrypted/mode";
+ struct stat st;
+ if (stat(mode_file, &st) != 0 || st.st_size <= 0) {
+ printf("Invalid encryption mode file %s\n", mode_file);
+ return false;
+ }
+ size_t mode_size = st.st_size;
+ char contents_encryption_mode[mode_size + 1];
+ memset((void*)contents_encryption_mode, 0, mode_size + 1);
+ int fd = open(mode_file, O_RDONLY);
+ if (fd < 0) {
+ printf("error opening '%s': %s\n", mode_file, strerror(errno));
+ return false;
+ }
+ if (read(fd, contents_encryption_mode, mode_size) != mode_size) {
+ printf("read error on '%s': %s\n", mode_file, strerror(errno));
+ close(fd);
+ return false;
+ }
+ close(fd);
+ if (!strcmp(contents_encryption_mode, "software")) {
+ encryption_mode = EXT4_ENCRYPTION_MODE_AES_256_XTS;
+ } else if (!strcmp(contents_encryption_mode, "ice")) {
+ encryption_mode = EXT4_ENCRYPTION_MODE_PRIVATE;
+ } else {
+ printf("Invalid encryption mode '%s'\n", contents_encryption_mode);
+ return false;
+ }
+ printf("set encryption mode to %i\n", encryption_mode);
+ return true;
+}
diff --git a/crypto/ext4crypt/ext4crypt_tar.h b/crypto/ext4crypt/ext4crypt_tar.h
new file mode 100644
index 000000000..c35d11571
--- /dev/null
+++ b/crypto/ext4crypt/ext4crypt_tar.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2016 Team Win Recovery Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __EXT4CRYPT_TAR_H
+#define __EXT4CRYPT_TAR_H
+
+#include <sys/cdefs.h>
+#include <stdbool.h>
+#include <cutils/multiuser.h>
+
+// ext4enc:TODO Include structure from somewhere sensible
+// MUST be in sync with ext4_crypto.c in kernel
+#define EXT4_KEY_DESCRIPTOR_SIZE 8
+#define EXT4_KEY_DESCRIPTOR_SIZE_HEX 17
+
+// ext4enc:TODO Get value from somewhere sensible
+#define EXT4_IOC_SET_ENCRYPTION_POLICY _IOR('f', 19, struct ext4_encryption_policy)
+#define EXT4_IOC_GET_ENCRYPTION_POLICY _IOW('f', 21, struct ext4_encryption_policy)
+
+__BEGIN_DECLS
+
+struct ext4_encryption_policy {
+ char version;
+ char contents_encryption_mode;
+ char filenames_encryption_mode;
+ char flags;
+ char master_key_descriptor[EXT4_KEY_DESCRIPTOR_SIZE];
+} __attribute__((__packed__));
+
+bool lookup_ref_key(const char* policy, char* policy_type);
+bool lookup_ref_tar(const char* policy_type, char* policy);
+
+void policy_to_hex(const char* policy, char* hex);
+bool e4crypt_policy_set(const char *directory, const char *policy,
+ size_t policy_length, int contents_encryption_mode);
+bool e4crypt_policy_get(const char *directory, char *policy,
+ size_t policy_length, int contents_encryption_mode);
+void e4crypt_policy_fill_default_struct(struct ext4_encryption_policy *eep);
+bool e4crypt_policy_set_struct(const char *directory, const struct ext4_encryption_policy *eep);
+bool e4crypt_policy_get_struct(const char *directory, struct ext4_encryption_policy *eep);
+
+bool e4crypt_set_mode();
+__END_DECLS
+
+#endif
diff --git a/crypto/ext4crypt/main.cpp b/crypto/ext4crypt/main.cpp
new file mode 100644
index 000000000..f0266ae10
--- /dev/null
+++ b/crypto/ext4crypt/main.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2016 Team Win Recovery Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "Decrypt.h"
+
+int main(int argc, char *argv[]) {
+ bool ret = false;
+ if (argc < 2) {
+ Decrypt_DE();
+ ret = Decrypt_User(0, "0000");
+ } else if (argc < 3) {
+ Decrypt_DE();
+ ret = Decrypt_User(0, argv[1]);
+ } else {
+ ret = Decrypt_User(atoi(argv[1]), argv[2]);
+ }
+ if (!ret)
+ printf("Failed to decrypt\n");
+ return 0;
+}
diff --git a/crypto/lollipop/Android.mk b/crypto/lollipop/Android.mk
new file mode 100644
index 000000000..6dc386a29
--- /dev/null
+++ b/crypto/lollipop/Android.mk
@@ -0,0 +1,60 @@
+LOCAL_PATH := $(call my-dir)
+ifeq ($(TW_INCLUDE_CRYPTO), true)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libcryptfslollipop
+LOCAL_MODULE_TAGS := eng optional
+LOCAL_CFLAGS :=
+LOCAL_SRC_FILES = cryptfs.c
+LOCAL_SHARED_LIBRARIES := libcrypto libhardware libcutils
+LOCAL_C_INCLUDES := external/openssl/include $(commands_recovery_local_path)/crypto/scrypt/lib/crypto
+
+ifeq ($(TARGET_HW_DISK_ENCRYPTION),true)
+ ifeq ($(TARGET_CRYPTFS_HW_PATH),)
+ LOCAL_C_INCLUDES += device/qcom/common/cryptfs_hw
+ else
+ LOCAL_C_INCLUDES += $(TARGET_CRYPTFS_HW_PATH)
+ endif
+ LOCAL_SHARED_LIBRARIES += libcryptfs_hw
+ LOCAL_CFLAGS += -DCONFIG_HW_DISK_ENCRYPTION
+endif
+
+ifneq ($(wildcard hardware/libhardware/include/hardware/keymaster0.h),)
+ LOCAL_CFLAGS += -DTW_CRYPTO_HAVE_KEYMASTERX
+ LOCAL_C_INCLUDES += external/boringssl/src/include
+endif
+
+LOCAL_WHOLE_STATIC_LIBRARIES += libscrypttwrp_static
+
+include $(BUILD_SHARED_LIBRARY)
+
+
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := twrpdec
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin
+LOCAL_SRC_FILES := main.c cryptfs.c
+LOCAL_SHARED_LIBRARIES := libcrypto libhardware libcutils libc
+LOCAL_C_INCLUDES := external/openssl/include $(commands_recovery_local_path)/crypto/scrypt/lib/crypto
+
+ifeq ($(TARGET_HW_DISK_ENCRYPTION),true)
+ ifeq ($(TARGET_CRYPTFS_HW_PATH),)
+ LOCAL_C_INCLUDES += device/qcom/common/cryptfs_hw
+ else
+ LOCAL_C_INCLUDES += $(TARGET_CRYPTFS_HW_PATH)
+ endif
+ LOCAL_SHARED_LIBRARIES += libcryptfs_hw
+ LOCAL_CFLAGS += -DCONFIG_HW_DISK_ENCRYPTION
+endif
+
+ifneq ($(wildcard hardware/libhardware/include/hardware/keymaster0.h),)
+ LOCAL_CFLAGS += -DTW_CRYPTO_HAVE_KEYMASTERX
+ LOCAL_C_INCLUDES += external/boringssl/src/include
+endif
+
+LOCAL_WHOLE_STATIC_LIBRARIES += libscrypttwrp_static
+include $(BUILD_EXECUTABLE)
+
+endif
diff --git a/crypto/lollipop/cryptfs.c b/crypto/lollipop/cryptfs.c
new file mode 100644
index 000000000..80f433bf6
--- /dev/null
+++ b/crypto/lollipop/cryptfs.c
@@ -0,0 +1,1609 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+/* TO DO:
+ * 1. Perhaps keep several copies of the encrypted key, in case something
+ * goes horribly wrong?
+ *
+ */
+
+#include <sys/types.h>
+#include <linux/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <linux/dm-ioctl.h>
+#include <libgen.h>
+#include <stdlib.h>
+#include <sys/param.h>
+#include <string.h>
+#include <sys/mount.h>
+#include <openssl/evp.h>
+#include <errno.h>
+#include <linux/kdev_t.h>
+#include <time.h>
+#include "cryptfs.h"
+#include "cutils/properties.h"
+#include "crypto_scrypt.h"
+
+#ifndef TW_CRYPTO_HAVE_KEYMASTERX
+#include <hardware/keymaster.h>
+#else
+#include <stdbool.h>
+#include <openssl/evp.h>
+#include <openssl/sha.h>
+#include <hardware/keymaster0.h>
+#include <hardware/keymaster1.h>
+#endif
+
+#ifndef min /* already defined by windows.h */
+#define min(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+#define UNUSED __attribute__((unused))
+
+#define UNUSED __attribute__((unused))
+
+#ifdef CONFIG_HW_DISK_ENCRYPTION
+#include "cryptfs_hw.h"
+#endif
+
+#define DM_CRYPT_BUF_SIZE 4096
+
+#define HASH_COUNT 2000
+#define KEY_LEN_BYTES 16
+#define IV_LEN_BYTES 16
+
+#define KEY_IN_FOOTER "footer"
+
+#define EXT4_FS 1
+#define F2FS_FS 2
+
+#define TABLE_LOAD_RETRIES 10
+
+#define RSA_KEY_SIZE 2048
+#define RSA_KEY_SIZE_BYTES (RSA_KEY_SIZE / 8)
+#define RSA_EXPONENT 0x10001
+#define KEYMASTER_CRYPTFS_RATE_LIMIT 1 // Maximum one try per second
+
+#define RETRY_MOUNT_ATTEMPTS 10
+#define RETRY_MOUNT_DELAY_SECONDS 1
+
+char *me = "cryptfs";
+
+static unsigned char saved_master_key[KEY_LEN_BYTES];
+static char *saved_mount_point;
+static int master_key_saved = 0;
+static struct crypt_persist_data *persist_data = NULL;
+static char key_fname[PROPERTY_VALUE_MAX] = "";
+static char real_blkdev[PROPERTY_VALUE_MAX] = "";
+static char file_system[PROPERTY_VALUE_MAX] = "";
+
+#ifdef CONFIG_HW_DISK_ENCRYPTION
+static int scrypt_keymaster(const char *passwd, const unsigned char *salt,
+ unsigned char *ikey, void *params);
+static void convert_key_to_hex_ascii(const unsigned char *master_key,
+ unsigned int keysize, char *master_key_ascii);
+static int get_keymaster_hw_fde_passwd(const char* passwd, unsigned char* newpw,
+ unsigned char* salt,
+ const struct crypt_mnt_ftr *ftr)
+{
+ /* if newpw updated, return 0
+ * if newpw not updated return -1
+ */
+ int rc = -1;
+
+ if (should_use_keymaster()) {
+ if (scrypt_keymaster(passwd, salt, newpw, (void*)ftr)) {
+ printf("scrypt failed");
+ } else {
+ rc = 0;
+ }
+ }
+
+ return rc;
+}
+
+static int verify_hw_fde_passwd(char *passwd, struct crypt_mnt_ftr* crypt_ftr)
+{
+ unsigned char newpw[32] = {0};
+ int key_index;
+ if (get_keymaster_hw_fde_passwd(passwd, newpw, crypt_ftr->salt, crypt_ftr))
+ key_index = set_hw_device_encryption_key(passwd,
+ (char*) crypt_ftr->crypto_type_name);
+ else
+ key_index = set_hw_device_encryption_key((const char*)newpw,
+ (char*) crypt_ftr->crypto_type_name);
+ return key_index;
+}
+#endif
+
+void set_partition_data(const char* block_device, const char* key_location, const char* fs)
+{
+ strcpy(key_fname, key_location);
+ strcpy(real_blkdev, block_device);
+ strcpy(file_system, fs);
+}
+
+#ifndef TW_CRYPTO_HAVE_KEYMASTERX
+static int keymaster_init(keymaster_device_t **keymaster_dev)
+{
+ int rc;
+
+ const hw_module_t* mod;
+ rc = hw_get_module_by_class(KEYSTORE_HARDWARE_MODULE_ID, NULL, &mod);
+ if (rc) {
+ printf("could not find any keystore module\n");
+ goto out;
+ }
+
+ rc = keymaster_open(mod, keymaster_dev);
+ if (rc) {
+ printf("could not open keymaster device in %s (%s)\n",
+ KEYSTORE_HARDWARE_MODULE_ID, strerror(-rc));
+ goto out;
+ }
+
+ return 0;
+
+out:
+ *keymaster_dev = NULL;
+ return rc;
+}
+
+/* Should we use keymaster? */
+static int keymaster_check_compatibility()
+{
+ keymaster_device_t *keymaster_dev = 0;
+ int rc = 0;
+
+ if (keymaster_init(&keymaster_dev)) {
+ printf("Failed to init keymaster\n");
+ rc = -1;
+ goto out;
+ }
+
+ printf("keymaster version is %d\n", keymaster_dev->common.module->module_api_version);
+
+#if (KEYMASTER_HEADER_VERSION >= 3)
+ if (keymaster_dev->common.module->module_api_version
+ < KEYMASTER_MODULE_API_VERSION_0_3) {
+ rc = 0;
+ goto out;
+ }
+
+ if (keymaster_dev->flags & KEYMASTER_BLOBS_ARE_STANDALONE) {
+ rc = 1;
+ }
+
+#endif
+out:
+ keymaster_close(keymaster_dev);
+ return rc;
+}
+
+/* Create a new keymaster key and store it in this footer */
+static int keymaster_create_key(struct crypt_mnt_ftr *ftr)
+{
+ uint8_t* key = 0;
+ keymaster_device_t *keymaster_dev = 0;
+
+ if (keymaster_init(&keymaster_dev)) {
+ printf("Failed to init keymaster\n");
+ return -1;
+ }
+
+ int rc = 0;
+
+ keymaster_rsa_keygen_params_t params;
+ memset(&params, '\0', sizeof(params));
+ params.public_exponent = RSA_EXPONENT;
+ params.modulus_size = RSA_KEY_SIZE;
+
+ size_t key_size;
+ if (keymaster_dev->generate_keypair(keymaster_dev, TYPE_RSA, &params,
+ &key, &key_size)) {
+ printf("Failed to generate keypair\n");
+ rc = -1;
+ goto out;
+ }
+
+ if (key_size > KEYMASTER_BLOB_SIZE) {
+ printf("Keymaster key too large for crypto footer\n");
+ rc = -1;
+ goto out;
+ }
+
+ memcpy(ftr->keymaster_blob, key, key_size);
+ ftr->keymaster_blob_size = key_size;
+
+out:
+ keymaster_close(keymaster_dev);
+ free(key);
+ return rc;
+}
+
+/* This signs the given object using the keymaster key. */
+static int keymaster_sign_object(struct crypt_mnt_ftr *ftr,
+ const unsigned char *object,
+ const size_t object_size,
+ unsigned char **signature,
+ size_t *signature_size)
+{
+ int rc = 0;
+ keymaster_device_t *keymaster_dev = 0;
+ if (keymaster_init(&keymaster_dev)) {
+ printf("Failed to init keymaster\n");
+ return -1;
+ }
+
+ /* We currently set the digest type to DIGEST_NONE because it's the
+ * only supported value for keymaster. A similar issue exists with
+ * PADDING_NONE. Long term both of these should likely change.
+ */
+ keymaster_rsa_sign_params_t params;
+ params.digest_type = DIGEST_NONE;
+ params.padding_type = PADDING_NONE;
+
+ unsigned char to_sign[RSA_KEY_SIZE_BYTES];
+ size_t to_sign_size = sizeof(to_sign);
+ memset(to_sign, 0, RSA_KEY_SIZE_BYTES);
+
+ // To sign a message with RSA, the message must satisfy two
+ // constraints:
+ //
+ // 1. The message, when interpreted as a big-endian numeric value, must
+ // be strictly less than the public modulus of the RSA key. Note
+ // that because the most significant bit of the public modulus is
+ // guaranteed to be 1 (else it's an (n-1)-bit key, not an n-bit
+ // key), an n-bit message with most significant bit 0 always
+ // satisfies this requirement.
+ //
+ // 2. The message must have the same length in bits as the public
+ // modulus of the RSA key. This requirement isn't mathematically
+ // necessary, but is necessary to ensure consistency in
+ // implementations.
+ switch (ftr->kdf_type) {
+ case KDF_SCRYPT_KEYMASTER_UNPADDED:
+ // This is broken: It produces a message which is shorter than
+ // the public modulus, failing criterion 2.
+ memcpy(to_sign, object, object_size);
+ to_sign_size = object_size;
+ printf("Signing unpadded object\n");
+ break;
+ case KDF_SCRYPT_KEYMASTER_BADLY_PADDED:
+ // This is broken: Since the value of object is uniformly
+ // distributed, it produces a message that is larger than the
+ // public modulus with probability 0.25.
+ memcpy(to_sign, object, min(RSA_KEY_SIZE_BYTES, object_size));
+ printf("Signing end-padded object\n");
+ break;
+ case KDF_SCRYPT_KEYMASTER:
+ // This ensures the most significant byte of the signed message
+ // is zero. We could have zero-padded to the left instead, but
+ // this approach is slightly more robust against changes in
+ // object size. However, it's still broken (but not unusably
+ // so) because we really should be using a proper RSA padding
+ // function, such as OAEP.
+ //
+ // TODO(paullawrence): When keymaster 0.4 is available, change
+ // this to use the padding options it provides.
+ memcpy(to_sign + 1, object, min(RSA_KEY_SIZE_BYTES - 1, object_size));
+ printf("Signing safely-padded object\n");
+ break;
+ default:
+ printf("Unknown KDF type %d\n", ftr->kdf_type);
+ return -1;
+ }
+
+ rc = keymaster_dev->sign_data(keymaster_dev,
+ &params,
+ ftr->keymaster_blob,
+ ftr->keymaster_blob_size,
+ to_sign,
+ to_sign_size,
+ signature,
+ signature_size);
+
+ keymaster_close(keymaster_dev);
+ return rc;
+}
+#else //#ifndef TW_CRYPTO_HAVE_KEYMASTERX
+static int keymaster_init(keymaster0_device_t **keymaster0_dev,
+ keymaster1_device_t **keymaster1_dev)
+{
+ int rc;
+
+ const hw_module_t* mod;
+ rc = hw_get_module_by_class(KEYSTORE_HARDWARE_MODULE_ID, NULL, &mod);
+ if (rc) {
+ printf("could not find any keystore module\n");
+ goto err;
+ }
+
+ printf("keymaster module name is %s\n", mod->name);
+ printf("keymaster version is %d\n", mod->module_api_version);
+
+ *keymaster0_dev = NULL;
+ *keymaster1_dev = NULL;
+ if (mod->module_api_version == KEYMASTER_MODULE_API_VERSION_1_0) {
+ printf("Found keymaster1 module, using keymaster1 API.\n");
+ rc = keymaster1_open(mod, keymaster1_dev);
+ } else {
+ printf("Found keymaster0 module, using keymaster0 API.\n");
+ rc = keymaster0_open(mod, keymaster0_dev);
+ }
+
+ if (rc) {
+ printf("could not open keymaster device in %s (%s)\n",
+ KEYSTORE_HARDWARE_MODULE_ID, strerror(-rc));
+ goto err;
+ }
+
+ return 0;
+
+err:
+ *keymaster0_dev = NULL;
+ *keymaster1_dev = NULL;
+ return rc;
+}
+
+/* Should we use keymaster? */
+static int keymaster_check_compatibility()
+{
+ keymaster0_device_t *keymaster0_dev = 0;
+ keymaster1_device_t *keymaster1_dev = 0;
+ int rc = 0;
+
+ if (keymaster_init(&keymaster0_dev, &keymaster1_dev)) {
+ printf("Failed to init keymaster\n");
+ rc = -1;
+ goto out;
+ }
+
+ if (keymaster1_dev) {
+ rc = 1;
+ goto out;
+ }
+
+ // TODO(swillden): Check to see if there's any reason to require v0.3. I think v0.1 and v0.2
+ // should work.
+ if (keymaster0_dev->common.module->module_api_version
+ < KEYMASTER_MODULE_API_VERSION_0_3) {
+ rc = 0;
+ goto out;
+ }
+
+ if (!(keymaster0_dev->flags & KEYMASTER_SOFTWARE_ONLY) &&
+ (keymaster0_dev->flags & KEYMASTER_BLOBS_ARE_STANDALONE)) {
+ rc = 1;
+ }
+
+out:
+ if (keymaster1_dev) {
+ keymaster1_close(keymaster1_dev);
+ }
+ if (keymaster0_dev) {
+ keymaster0_close(keymaster0_dev);
+ }
+ return rc;
+}
+
+/* Create a new keymaster key and store it in this footer */
+static int keymaster_create_key(struct crypt_mnt_ftr *ftr)
+{
+ uint8_t* key = 0;
+ keymaster0_device_t *keymaster0_dev = 0;
+ keymaster1_device_t *keymaster1_dev = 0;
+
+ if (keymaster_init(&keymaster0_dev, &keymaster1_dev)) {
+ printf("Failed to init keymaster\n");
+ return -1;
+ }
+
+ int rc = 0;
+ size_t key_size = 0;
+ if (keymaster1_dev) {
+ keymaster_key_param_t params[] = {
+ /* Algorithm & size specifications. Stick with RSA for now. Switch to AES later. */
+ keymaster_param_enum(KM_TAG_ALGORITHM, KM_ALGORITHM_RSA),
+ keymaster_param_int(KM_TAG_KEY_SIZE, RSA_KEY_SIZE),
+ keymaster_param_long(KM_TAG_RSA_PUBLIC_EXPONENT, RSA_EXPONENT),
+
+ /* The only allowed purpose for this key is signing. */
+ keymaster_param_enum(KM_TAG_PURPOSE, KM_PURPOSE_SIGN),
+
+ /* Padding & digest specifications. */
+ keymaster_param_enum(KM_TAG_PADDING, KM_PAD_NONE),
+ keymaster_param_enum(KM_TAG_DIGEST, KM_DIGEST_NONE),
+
+ /* Require that the key be usable in standalone mode. File system isn't available. */
+ keymaster_param_enum(KM_TAG_BLOB_USAGE_REQUIREMENTS, KM_BLOB_STANDALONE),
+
+ /* No auth requirements, because cryptfs is not yet integrated with gatekeeper. */
+ keymaster_param_bool(KM_TAG_NO_AUTH_REQUIRED),
+
+ /* Rate-limit key usage attempts, to rate-limit brute force */
+ keymaster_param_int(KM_TAG_MIN_SECONDS_BETWEEN_OPS, KEYMASTER_CRYPTFS_RATE_LIMIT),
+ };
+ keymaster_key_param_set_t param_set = { params, sizeof(params)/sizeof(*params) };
+ keymaster_key_blob_t key_blob;
+ keymaster_error_t error = keymaster1_dev->generate_key(keymaster1_dev, &param_set,
+ &key_blob,
+ NULL /* characteristics */);
+ if (error != KM_ERROR_OK) {
+ printf("Failed to generate keymaster1 key, error %d\n", error);
+ rc = -1;
+ goto out;
+ }
+
+ key = (uint8_t*)key_blob.key_material;
+ key_size = key_blob.key_material_size;
+ }
+ else if (keymaster0_dev) {
+ keymaster_rsa_keygen_params_t params;
+ memset(&params, '\0', sizeof(params));
+ params.public_exponent = RSA_EXPONENT;
+ params.modulus_size = RSA_KEY_SIZE;
+
+ if (keymaster0_dev->generate_keypair(keymaster0_dev, TYPE_RSA, &params,
+ &key, &key_size)) {
+ printf("Failed to generate keypair\n");
+ rc = -1;
+ goto out;
+ }
+ } else {
+ printf("Cryptfs bug: keymaster_init succeeded but didn't initialize a device\n");
+ rc = -1;
+ goto out;
+ }
+
+ if (key_size > KEYMASTER_BLOB_SIZE) {
+ printf("Keymaster key too large for crypto footer\n");
+ rc = -1;
+ goto out;
+ }
+
+ memcpy(ftr->keymaster_blob, key, key_size);
+ ftr->keymaster_blob_size = key_size;
+
+out:
+ if (keymaster0_dev)
+ keymaster0_close(keymaster0_dev);
+ if (keymaster1_dev)
+ keymaster1_close(keymaster1_dev);
+ free(key);
+ return rc;
+}
+
+/* This signs the given object using the keymaster key. */
+static int keymaster_sign_object(struct crypt_mnt_ftr *ftr,
+ const unsigned char *object,
+ const size_t object_size,
+ unsigned char **signature,
+ size_t *signature_size)
+{
+ int rc = 0;
+ keymaster0_device_t *keymaster0_dev = 0;
+ keymaster1_device_t *keymaster1_dev = 0;
+ if (keymaster_init(&keymaster0_dev, &keymaster1_dev)) {
+ printf("Failed to init keymaster\n");
+ rc = -1;
+ goto out;
+ }
+
+ unsigned char to_sign[RSA_KEY_SIZE_BYTES];
+ size_t to_sign_size = sizeof(to_sign);
+ memset(to_sign, 0, RSA_KEY_SIZE_BYTES);
+
+ // To sign a message with RSA, the message must satisfy two
+ // constraints:
+ //
+ // 1. The message, when interpreted as a big-endian numeric value, must
+ // be strictly less than the public modulus of the RSA key. Note
+ // that because the most significant bit of the public modulus is
+ // guaranteed to be 1 (else it's an (n-1)-bit key, not an n-bit
+ // key), an n-bit message with most significant bit 0 always
+ // satisfies this requirement.
+ //
+ // 2. The message must have the same length in bits as the public
+ // modulus of the RSA key. This requirement isn't mathematically
+ // necessary, but is necessary to ensure consistency in
+ // implementations.
+ switch (ftr->kdf_type) {
+ case KDF_SCRYPT_KEYMASTER:
+ // This ensures the most significant byte of the signed message
+ // is zero. We could have zero-padded to the left instead, but
+ // this approach is slightly more robust against changes in
+ // object size. However, it's still broken (but not unusably
+ // so) because we really should be using a proper deterministic
+ // RSA padding function, such as PKCS1.
+ memcpy(to_sign + 1, object, min(RSA_KEY_SIZE_BYTES - 1, object_size));
+ printf("Signing safely-padded object\n");
+ break;
+ default:
+ printf("Unknown KDF type %d\n", ftr->kdf_type);
+ rc = -1;
+ goto out;
+ }
+
+ if (keymaster0_dev) {
+ keymaster_rsa_sign_params_t params;
+ params.digest_type = DIGEST_NONE;
+ params.padding_type = PADDING_NONE;
+
+ rc = keymaster0_dev->sign_data(keymaster0_dev,
+ &params,
+ ftr->keymaster_blob,
+ ftr->keymaster_blob_size,
+ to_sign,
+ to_sign_size,
+ signature,
+ signature_size);
+ goto out;
+ } else if (keymaster1_dev) {
+ keymaster_key_blob_t key = { ftr->keymaster_blob, ftr->keymaster_blob_size };
+ keymaster_key_param_t params[] = {
+ keymaster_param_enum(KM_TAG_PADDING, KM_PAD_NONE),
+ keymaster_param_enum(KM_TAG_DIGEST, KM_DIGEST_NONE),
+ };
+ keymaster_key_param_set_t param_set = { params, sizeof(params)/sizeof(*params) };
+ keymaster_operation_handle_t op_handle;
+ keymaster_error_t error = keymaster1_dev->begin(keymaster1_dev, KM_PURPOSE_SIGN, &key,
+ &param_set, NULL /* out_params */,
+ &op_handle);
+ if (error == KM_ERROR_KEY_RATE_LIMIT_EXCEEDED) {
+ // Key usage has been rate-limited. Wait a bit and try again.
+ sleep(KEYMASTER_CRYPTFS_RATE_LIMIT);
+ error = keymaster1_dev->begin(keymaster1_dev, KM_PURPOSE_SIGN, &key,
+ &param_set, NULL /* out_params */,
+ &op_handle);
+ }
+ if (error != KM_ERROR_OK) {
+ printf("Error starting keymaster signature transaction: %d\n", error);
+ rc = -1;
+ goto out;
+ }
+
+ keymaster_blob_t input = { to_sign, to_sign_size };
+ size_t input_consumed;
+ error = keymaster1_dev->update(keymaster1_dev, op_handle, NULL /* in_params */,
+ &input, &input_consumed, NULL /* out_params */,
+ NULL /* output */);
+ if (error != KM_ERROR_OK) {
+ printf("Error sending data to keymaster signature transaction: %d\n", error);
+ rc = -1;
+ goto out;
+ }
+ if (input_consumed != to_sign_size) {
+ // This should never happen. If it does, it's a bug in the keymaster implementation.
+ printf("Keymaster update() did not consume all data.\n");
+ keymaster1_dev->abort(keymaster1_dev, op_handle);
+ rc = -1;
+ goto out;
+ }
+
+ keymaster_blob_t tmp_sig;
+ error = keymaster1_dev->finish(keymaster1_dev, op_handle, NULL /* in_params */,
+ NULL /* verify signature */, NULL /* out_params */,
+ &tmp_sig);
+ if (error != KM_ERROR_OK) {
+ printf("Error finishing keymaster signature transaction: %d\n", error);
+ rc = -1;
+ goto out;
+ }
+
+ *signature = (uint8_t*)tmp_sig.data;
+ *signature_size = tmp_sig.data_length;
+ } else {
+ printf("Cryptfs bug: keymaster_init succeded but didn't initialize a device.\n");
+ rc = -1;
+ goto out;
+ }
+
+ out:
+ if (keymaster1_dev)
+ keymaster1_close(keymaster1_dev);
+ if (keymaster0_dev)
+ keymaster0_close(keymaster0_dev);
+
+ return rc;
+}
+#endif //#ifndef TW_CRYPTO_HAVE_KEYMASTERX
+
+/* Store password when userdata is successfully decrypted and mounted.
+ * Cleared by cryptfs_clear_password
+ *
+ * To avoid a double prompt at boot, we need to store the CryptKeeper
+ * password and pass it to KeyGuard, which uses it to unlock KeyStore.
+ * Since the entire framework is torn down and rebuilt after encryption,
+ * we have to use a daemon or similar to store the password. Since vold
+ * is secured against IPC except from system processes, it seems a reasonable
+ * place to store this.
+ *
+ * password should be cleared once it has been used.
+ *
+ * password is aged out after password_max_age_seconds seconds.
+ */
+static char* password = 0;
+static int password_expiry_time = 0;
+static const int password_max_age_seconds = 60;
+
+static void ioctl_init(struct dm_ioctl *io, size_t dataSize, const char *name, unsigned flags)
+{
+ memset(io, 0, dataSize);
+ io->data_size = dataSize;
+ io->data_start = sizeof(struct dm_ioctl);
+ io->version[0] = 4;
+ io->version[1] = 0;
+ io->version[2] = 0;
+ io->flags = flags;
+ if (name) {
+ strncpy(io->name, name, sizeof(io->name));
+ }
+}
+
+/**
+ * Gets the default device scrypt parameters for key derivation time tuning.
+ * The parameters should lead to about one second derivation time for the
+ * given device.
+ */
+static void get_device_scrypt_params(struct crypt_mnt_ftr *ftr) {
+ const int default_params[] = SCRYPT_DEFAULTS;
+ int params[] = SCRYPT_DEFAULTS;
+ char paramstr[PROPERTY_VALUE_MAX];
+ char *token;
+ char *saveptr;
+ int i;
+
+ property_get(SCRYPT_PROP, paramstr, "");
+ if (paramstr[0] != '\0') {
+ /*
+ * The token we're looking for should be three integers separated by
+ * colons (e.g., "12:8:1"). Scan the property to make sure it matches.
+ */
+ for (i = 0, token = strtok_r(paramstr, ":", &saveptr);
+ token != NULL && i < 3;
+ i++, token = strtok_r(NULL, ":", &saveptr)) {
+ char *endptr;
+ params[i] = strtol(token, &endptr, 10);
+
+ /*
+ * Check that there was a valid number and it's 8-bit. If not,
+ * break out and the end check will take the default values.
+ */
+ if ((*token == '\0') || (*endptr != '\0') || params[i] < 0 || params[i] > 255) {
+ break;
+ }
+ }
+
+ /*
+ * If there were not enough tokens or a token was malformed (not an
+ * integer), it will end up here and the default parameters can be
+ * taken.
+ */
+ if ((i != 3) || (token != NULL)) {
+ printf("bad scrypt parameters '%s' should be like '12:8:1'; using defaults\n", paramstr);
+ memcpy(params, default_params, sizeof(params));
+ }
+ }
+
+ ftr->N_factor = params[0];
+ ftr->r_factor = params[1];
+ ftr->p_factor = params[2];
+}
+
+static unsigned int get_blkdev_size(int fd)
+{
+ unsigned long nr_sec;
+
+ if ( (ioctl(fd, BLKGETSIZE, &nr_sec)) == -1) {
+ nr_sec = 0;
+ }
+
+ return (unsigned int) nr_sec;
+}
+
+static int get_crypt_ftr_info(char **metadata_fname, off64_t *off)
+{
+ static int cached_data = 0;
+ static off64_t cached_off = 0;
+ static char cached_metadata_fname[PROPERTY_VALUE_MAX] = "";
+ int fd;
+ unsigned int nr_sec;
+ int rc = -1;
+
+ if (!cached_data) {
+ printf("get_crypt_ftr_info crypto key location: '%s'\n", key_fname);
+ if (!strcmp(key_fname, KEY_IN_FOOTER)) {
+ if ( (fd = open(real_blkdev, O_RDWR)) < 0) {
+ printf("Cannot open real block device %s\n", real_blkdev);
+ return -1;
+ }
+
+ if ((nr_sec = get_blkdev_size(fd))) {
+ /* If it's an encrypted Android partition, the last 16 Kbytes contain the
+ * encryption info footer and key, and plenty of bytes to spare for future
+ * growth.
+ */
+ strlcpy(cached_metadata_fname, real_blkdev, sizeof(cached_metadata_fname));
+ cached_off = ((off64_t)nr_sec * 512) - CRYPT_FOOTER_OFFSET;
+ cached_data = 1;
+ } else {
+ printf("Cannot get size of block device %s\n", real_blkdev);
+ }
+ close(fd);
+ } else {
+ strlcpy(cached_metadata_fname, key_fname, sizeof(cached_metadata_fname));
+ cached_off = 0;
+ cached_data = 1;
+ }
+ }
+
+ if (cached_data) {
+ if (metadata_fname) {
+ *metadata_fname = cached_metadata_fname;
+ }
+ if (off) {
+ *off = cached_off;
+ }
+ rc = 0;
+ }
+
+ return rc;
+}
+
+static int get_crypt_ftr_and_key(struct crypt_mnt_ftr *crypt_ftr)
+{
+ int fd;
+ unsigned int nr_sec, cnt;
+ off64_t starting_off;
+ int rc = -1;
+ char *fname = NULL;
+ struct stat statbuf;
+
+ if (get_crypt_ftr_info(&fname, &starting_off)) {
+ printf("Unable to get crypt_ftr_info\n");
+ return -1;
+ }
+ if (fname[0] != '/') {
+ printf("Unexpected value for crypto key location\n");
+ return -1;
+ }
+ if ( (fd = open(fname, O_RDWR)) < 0) {
+ printf("Cannot open footer file %s for get\n", fname);
+ return -1;
+ }
+
+ /* Make sure it's 16 Kbytes in length */
+ fstat(fd, &statbuf);
+ if (S_ISREG(statbuf.st_mode) && (statbuf.st_size != 0x4000)) {
+ printf("footer file %s is not the expected size!\n", fname);
+ goto errout;
+ }
+
+ /* Seek to the start of the crypt footer */
+ if (lseek64(fd, starting_off, SEEK_SET) == -1) {
+ printf("Cannot seek to real block device footer\n");
+ goto errout;
+ }
+
+ if ( (cnt = read(fd, crypt_ftr, sizeof(struct crypt_mnt_ftr))) != sizeof(struct crypt_mnt_ftr)) {
+ printf("Cannot read real block device footer\n");
+ goto errout;
+ }
+
+ if (crypt_ftr->magic != CRYPT_MNT_MAGIC) {
+ printf("Bad magic for real block device %s\n", fname);
+ goto errout;
+ }
+
+ if (crypt_ftr->major_version != CURRENT_MAJOR_VERSION) {
+ printf("Cannot understand major version %d real block device footer; expected %d\n",
+ crypt_ftr->major_version, CURRENT_MAJOR_VERSION);
+ goto errout;
+ }
+
+ if (crypt_ftr->minor_version > CURRENT_MINOR_VERSION) {
+ printf("Warning: crypto footer minor version %d, expected <= %d, continuing...\n",
+ crypt_ftr->minor_version, CURRENT_MINOR_VERSION);
+ }
+
+ /* If this is a verion 1.0 crypt_ftr, make it a 1.1 crypt footer, and update the
+ * copy on disk before returning.
+ */
+ /*if (crypt_ftr->minor_version < CURRENT_MINOR_VERSION) {
+ upgrade_crypt_ftr(fd, crypt_ftr, starting_off);
+ }*/
+
+ /* Success! */
+ rc = 0;
+
+errout:
+ close(fd);
+ return rc;
+}
+
+static int hexdigit (char c)
+{
+ if (c >= '0' && c <= '9') return c - '0';
+ c = tolower(c);
+ if (c >= 'a' && c <= 'f') return c - 'a' + 10;
+ return -1;
+}
+
+static unsigned char* convert_hex_ascii_to_key(const char* master_key_ascii,
+ unsigned int* out_keysize)
+{
+ unsigned int i;
+ *out_keysize = 0;
+
+ size_t size = strlen (master_key_ascii);
+ if (size % 2) {
+ printf("Trying to convert ascii string of odd length\n");
+ return NULL;
+ }
+
+ unsigned char* master_key = (unsigned char*) malloc(size / 2);
+ if (master_key == 0) {
+ printf("Cannot allocate\n");
+ return NULL;
+ }
+
+ for (i = 0; i < size; i += 2) {
+ int high_nibble = hexdigit (master_key_ascii[i]);
+ int low_nibble = hexdigit (master_key_ascii[i + 1]);
+
+ if(high_nibble < 0 || low_nibble < 0) {
+ printf("Invalid hex string\n");
+ free (master_key);
+ return NULL;
+ }
+
+ master_key[*out_keysize] = high_nibble * 16 + low_nibble;
+ (*out_keysize)++;
+ }
+
+ return master_key;
+}
+
+/* Convert a binary key of specified length into an ascii hex string equivalent,
+ * without the leading 0x and with null termination
+ */
+static void convert_key_to_hex_ascii(const unsigned char *master_key,
+ unsigned int keysize, char *master_key_ascii) {
+ unsigned int i, a;
+ unsigned char nibble;
+
+ for (i=0, a=0; i<keysize; i++, a+=2) {
+ /* For each byte, write out two ascii hex digits */
+ nibble = (master_key[i] >> 4) & 0xf;
+ master_key_ascii[a] = nibble + (nibble > 9 ? 0x37 : 0x30);
+
+ nibble = master_key[i] & 0xf;
+ master_key_ascii[a+1] = nibble + (nibble > 9 ? 0x37 : 0x30);
+ }
+
+ /* Add the null termination */
+ master_key_ascii[a] = '\0';
+
+}
+
+static int load_crypto_mapping_table(struct crypt_mnt_ftr *crypt_ftr, const unsigned char *master_key,
+ const char *real_blk_name, const char *name, int fd,
+ char *extra_params)
+{
+ char buffer[DM_CRYPT_BUF_SIZE];
+ struct dm_ioctl *io;
+ struct dm_target_spec *tgt;
+ char *crypt_params;
+ char master_key_ascii[129]; /* Large enough to hold 512 bit key and null */
+ int i;
+
+ io = (struct dm_ioctl *) buffer;
+
+ /* Load the mapping table for this device */
+ tgt = (struct dm_target_spec *) &buffer[sizeof(struct dm_ioctl)];
+
+ ioctl_init(io, DM_CRYPT_BUF_SIZE, name, 0);
+ io->target_count = 1;
+ tgt->status = 0;
+ tgt->sector_start = 0;
+ tgt->length = crypt_ftr->fs_size;
+ crypt_params = buffer + sizeof(struct dm_ioctl) + sizeof(struct dm_target_spec);
+
+#ifdef CONFIG_HW_DISK_ENCRYPTION
+ if(is_hw_disk_encryption((char*)crypt_ftr->crypto_type_name)) {
+ strlcpy(tgt->target_type, "req-crypt",DM_MAX_TYPE_NAME);
+ if (is_ice_enabled())
+ convert_key_to_hex_ascii(master_key, sizeof(int), master_key_ascii);
+ else
+ convert_key_to_hex_ascii(master_key, crypt_ftr->keysize, master_key_ascii);
+ }
+ else {
+ convert_key_to_hex_ascii(master_key, crypt_ftr->keysize, master_key_ascii);
+ strlcpy(tgt->target_type, "crypt", DM_MAX_TYPE_NAME);
+ }
+#else
+ convert_key_to_hex_ascii(master_key, crypt_ftr->keysize, master_key_ascii);
+ strlcpy(tgt->target_type, "crypt", DM_MAX_TYPE_NAME);
+#endif
+
+ sprintf(crypt_params, "%s %s 0 %s 0 %s", crypt_ftr->crypto_type_name,
+ master_key_ascii, real_blk_name, extra_params);
+
+ printf("%s: target_type = %s\n", __func__, tgt->target_type);
+ printf("%s: real_blk_name = %s, extra_params = %s\n", __func__, real_blk_name, extra_params);
+
+ crypt_params += strlen(crypt_params) + 1;
+ crypt_params = (char *) (((unsigned long)crypt_params + 7) & ~8); /* Align to an 8 byte boundary */
+ tgt->next = crypt_params - buffer;
+
+ for (i = 0; i < TABLE_LOAD_RETRIES; i++) {
+ if (! ioctl(fd, DM_TABLE_LOAD, io)) {
+ break;
+ }
+ printf("%i\n", errno);
+ usleep(500000);
+ }
+
+ if (i == TABLE_LOAD_RETRIES) {
+ /* We failed to load the table, return an error */
+ return -1;
+ } else {
+ return i + 1;
+ }
+}
+
+
+static int get_dm_crypt_version(int fd, const char *name, int *version)
+{
+ char buffer[DM_CRYPT_BUF_SIZE];
+ struct dm_ioctl *io;
+ struct dm_target_versions *v;
+ int flag;
+ int i;
+
+ io = (struct dm_ioctl *) buffer;
+
+ ioctl_init(io, DM_CRYPT_BUF_SIZE, name, 0);
+
+ if (ioctl(fd, DM_LIST_VERSIONS, io)) {
+ return -1;
+ }
+
+ /* Iterate over the returned versions, looking for name of "crypt".
+ * When found, get and return the version.
+ */
+ v = (struct dm_target_versions *) &buffer[sizeof(struct dm_ioctl)];
+ while (v->next) {
+#ifdef CONFIG_HW_DISK_ENCRYPTION
+ if (is_hw_fde_enabled()) {
+ flag = (!strcmp(v->name, "crypt") || !strcmp(v->name, "req-crypt"));
+ } else {
+ flag = (!strcmp(v->name, "crypt"));
+ }
+ printf("get_dm_crypt_version flag: %i, name: '%s'\n", flag, v->name);
+ if (flag) {
+#else
+ if (! strcmp(v->name, "crypt")) {
+#endif
+ /* We found the crypt driver, return the version, and get out */
+ version[0] = v->version[0];
+ version[1] = v->version[1];
+ version[2] = v->version[2];
+ return 0;
+ }
+ v = (struct dm_target_versions *)(((char *)v) + v->next);
+ }
+
+ return -1;
+}
+
+static int create_crypto_blk_dev(struct crypt_mnt_ftr *crypt_ftr, const unsigned char *master_key,
+ const char *real_blk_name, char *crypto_blk_name, const char *name)
+{
+ char buffer[DM_CRYPT_BUF_SIZE];
+ char master_key_ascii[129]; /* Large enough to hold 512 bit key and null */
+ char *crypt_params;
+ struct dm_ioctl *io;
+ struct dm_target_spec *tgt;
+ unsigned int minor;
+ int fd=0;
+ int i;
+ int retval = -1;
+ int version[3];
+ char *extra_params;
+ int load_count;
+#ifdef CONFIG_HW_DISK_ENCRYPTION
+ char encrypted_state[PROPERTY_VALUE_MAX] = {0};
+ char progress[PROPERTY_VALUE_MAX] = {0};
+#endif
+
+ if ((fd = open("/dev/device-mapper", O_RDWR)) < 0 ) {
+ printf("Cannot open device-mapper\n");
+ goto errout;
+ }
+
+ io = (struct dm_ioctl *) buffer;
+
+ ioctl_init(io, DM_CRYPT_BUF_SIZE, name, 0);
+ if (ioctl(fd, DM_DEV_CREATE, io)) {
+ printf("Cannot create dm-crypt device %i\n", errno);
+ goto errout;
+ }
+
+ /* Get the device status, in particular, the name of it's device file */
+ ioctl_init(io, DM_CRYPT_BUF_SIZE, name, 0);
+ if (ioctl(fd, DM_DEV_STATUS, io)) {
+ printf("Cannot retrieve dm-crypt device status\n");
+ goto errout;
+ }
+ minor = (io->dev & 0xff) | ((io->dev >> 12) & 0xfff00);
+ snprintf(crypto_blk_name, MAXPATHLEN, "/dev/block/dm-%u", minor);
+
+#ifdef CONFIG_HW_DISK_ENCRYPTION
+ if(is_hw_disk_encryption((char*)crypt_ftr->crypto_type_name)) {
+ /* Set fde_enabled if either FDE completed or in-progress */
+ property_get("ro.crypto.state", encrypted_state, ""); /* FDE completed */
+ property_get("vold.encrypt_progress", progress, ""); /* FDE in progress */
+ if (!strcmp(encrypted_state, "encrypted") || strcmp(progress, "")) {
+ if (is_ice_enabled())
+ extra_params = "fde_enabled ice";
+ else
+ extra_params = "fde_enabled";
+ } else
+ extra_params = "fde_disabled";
+ } else {
+ extra_params = "";
+ if (! get_dm_crypt_version(fd, name, version)) {
+ /* Support for allow_discards was added in version 1.11.0 */
+ if ((version[0] >= 2) ||
+ ((version[0] == 1) && (version[1] >= 11))) {
+ extra_params = "1 allow_discards";
+ printf("Enabling support for allow_discards in dmcrypt.\n");
+ }
+ }
+ }
+#else
+ extra_params = "";
+ if (! get_dm_crypt_version(fd, name, version)) {
+ /* Support for allow_discards was added in version 1.11.0 */
+ if ((version[0] >= 2) ||
+ ((version[0] == 1) && (version[1] >= 11))) {
+ extra_params = "1 allow_discards";
+ printf("Enabling support for allow_discards in dmcrypt.\n");
+ }
+ }
+#endif
+
+ load_count = load_crypto_mapping_table(crypt_ftr, master_key, real_blk_name, name,
+ fd, extra_params);
+ if (load_count < 0) {
+ printf("Cannot load dm-crypt mapping table.\n");
+
+ // Remove the dm-crypt device, otherwise it cannot be used later on by other
+ // processes (eg vold_decrypt) or further testing/debugging in recovery
+ ioctl_init(io, DM_CRYPT_BUF_SIZE, name, 0);
+ if (ioctl(fd, DM_DEV_REMOVE, io)) {
+ printf("Cannot remove dm-crypt device %i\n", errno);
+ }
+ goto errout;
+ } else if (load_count > 1) {
+ printf("Took %d tries to load dmcrypt table.\n", load_count);
+ }
+
+ /* Resume this device to activate it */
+ ioctl_init(io, DM_CRYPT_BUF_SIZE, name, 0);
+
+ if (ioctl(fd, DM_DEV_SUSPEND, io)) {
+ printf("Cannot resume the dm-crypt device\n");
+
+ // Remove the dm-crypt device, otherwise it cannot be used later on by other
+ // processes (eg vold_decrypt) or further testing/debugging in recovery
+ ioctl_init(io, DM_CRYPT_BUF_SIZE, name, 0);
+ if (ioctl(fd, DM_DEV_REMOVE, io)) {
+ printf("Cannot remove dm-crypt device %i\n", errno);
+ }
+ goto errout;
+ }
+
+ /* We made it here with no errors. Woot! */
+ retval = 0;
+
+errout:
+ close(fd); /* If fd is <0 from a failed open call, it's safe to just ignore the close error */
+
+ return retval;
+}
+
+int delete_crypto_blk_dev(char *name)
+{
+ int fd;
+ char buffer[DM_CRYPT_BUF_SIZE];
+ struct dm_ioctl *io;
+ int retval = -1;
+
+ if ((fd = open("/dev/device-mapper", O_RDWR)) < 0 ) {
+ printf("Cannot open device-mapper\n");
+ goto errout;
+ }
+
+ io = (struct dm_ioctl *) buffer;
+
+ ioctl_init(io, DM_CRYPT_BUF_SIZE, name, 0);
+ if (ioctl(fd, DM_DEV_REMOVE, io)) {
+ printf("Cannot remove dm-crypt device\n");
+ goto errout;
+ }
+
+ /* We made it here with no errors. Woot! */
+ retval = 0;
+
+errout:
+ close(fd); /* If fd is <0 from a failed open call, it's safe to just ignore the close error */
+
+ return retval;
+
+}
+
+static int pbkdf2(const char *passwd, const unsigned char *salt,
+ unsigned char *ikey, void *params UNUSED)
+{
+ printf("Using pbkdf2 for cryptfs KDF\n");
+
+ /* Turn the password into a key and IV that can decrypt the master key */
+ unsigned int keysize;
+ char* master_key = (char*)convert_hex_ascii_to_key(passwd, &keysize);
+ if (!master_key) return -1;
+ PKCS5_PBKDF2_HMAC_SHA1(master_key, keysize, salt, SALT_LEN,
+ HASH_COUNT, KEY_LEN_BYTES+IV_LEN_BYTES, ikey);
+
+ memset(master_key, 0, keysize);
+ free (master_key);
+ return 0;
+}
+
+static int scrypt(const char *passwd, const unsigned char *salt,
+ unsigned char *ikey, void *params)
+{
+ printf("Using scrypt for cryptfs KDF\n");
+
+ struct crypt_mnt_ftr *ftr = (struct crypt_mnt_ftr *) params;
+
+ int N = 1 << ftr->N_factor;
+ int r = 1 << ftr->r_factor;
+ int p = 1 << ftr->p_factor;
+
+ /* Turn the password into a key and IV that can decrypt the master key */
+ unsigned int keysize;
+ unsigned char* master_key = convert_hex_ascii_to_key(passwd, &keysize);
+ if (!master_key) return -1;
+ crypto_scrypt(master_key, keysize, salt, SALT_LEN, N, r, p, ikey,
+ KEY_LEN_BYTES + IV_LEN_BYTES);
+
+ memset(master_key, 0, keysize);
+ free (master_key);
+ return 0;
+}
+
+static int scrypt_keymaster(const char *passwd, const unsigned char *salt,
+ unsigned char *ikey, void *params)
+{
+ printf("Using scrypt with keymaster for cryptfs KDF\n");
+
+ int rc;
+ unsigned int key_size;
+ size_t signature_size;
+ unsigned char* signature;
+ struct crypt_mnt_ftr *ftr = (struct crypt_mnt_ftr *) params;
+
+ int N = 1 << ftr->N_factor;
+ int r = 1 << ftr->r_factor;
+ int p = 1 << ftr->p_factor;
+
+ unsigned char* master_key = convert_hex_ascii_to_key(passwd, &key_size);
+ if (!master_key) {
+ printf("Failed to convert passwd from hex, using passwd instead\n");
+ master_key = strdup(passwd);
+ }
+
+ rc = crypto_scrypt(master_key, key_size, salt, SALT_LEN,
+ N, r, p, ikey, KEY_LEN_BYTES + IV_LEN_BYTES);
+ memset(master_key, 0, key_size);
+ free(master_key);
+
+ if (rc) {
+ printf("scrypt failed\n");
+ return -1;
+ }
+
+ if (keymaster_sign_object(ftr, ikey, KEY_LEN_BYTES + IV_LEN_BYTES,
+ &signature, &signature_size)) {
+ printf("Signing failed\n");
+ return -1;
+ }
+
+ rc = crypto_scrypt(signature, signature_size, salt, SALT_LEN,
+ N, r, p, ikey, KEY_LEN_BYTES + IV_LEN_BYTES);
+ free(signature);
+
+ if (rc) {
+ printf("scrypt failed\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int decrypt_master_key_aux(char *passwd, unsigned char *salt,
+ unsigned char *encrypted_master_key,
+ unsigned char *decrypted_master_key,
+ kdf_func kdf, void *kdf_params,
+ unsigned char** intermediate_key,
+ size_t* intermediate_key_size)
+{
+ unsigned char ikey[32+32] = { 0 }; /* Big enough to hold a 256 bit key and 256 bit IV */
+ EVP_CIPHER_CTX d_ctx;
+ int decrypted_len, final_len;
+
+ /* Turn the password into an intermediate key and IV that can decrypt the
+ master key */
+ if (kdf(passwd, salt, ikey, kdf_params)) {
+ printf("kdf failed\n");
+ return -1;
+ }
+
+ /* Initialize the decryption engine */
+ if (! EVP_DecryptInit(&d_ctx, EVP_aes_128_cbc(), ikey, ikey+KEY_LEN_BYTES)) {
+ return -1;
+ }
+ EVP_CIPHER_CTX_set_padding(&d_ctx, 0); /* Turn off padding as our data is block aligned */
+ /* Decrypt the master key */
+ if (! EVP_DecryptUpdate(&d_ctx, decrypted_master_key, &decrypted_len,
+ encrypted_master_key, KEY_LEN_BYTES)) {
+ return -1;
+ }
+#ifndef TW_CRYPTO_HAVE_KEYMASTERX
+ if (! EVP_DecryptFinal(&d_ctx, decrypted_master_key + decrypted_len, &final_len)) {
+#else
+ if (! EVP_DecryptFinal_ex(&d_ctx, decrypted_master_key + decrypted_len, &final_len)) {
+#endif
+ return -1;
+ }
+
+ if (decrypted_len + final_len != KEY_LEN_BYTES) {
+ return -1;
+ }
+
+ /* Copy intermediate key if needed by params */
+ if (intermediate_key && intermediate_key_size) {
+ *intermediate_key = (unsigned char*) malloc(KEY_LEN_BYTES);
+ if (intermediate_key) {
+ memcpy(*intermediate_key, ikey, KEY_LEN_BYTES);
+ *intermediate_key_size = KEY_LEN_BYTES;
+ }
+ }
+
+ return 0;
+}
+
+static void get_kdf_func(struct crypt_mnt_ftr *ftr, kdf_func *kdf, void** kdf_params)
+{
+ if (ftr->kdf_type == KDF_SCRYPT_KEYMASTER_UNPADDED ||
+ ftr->kdf_type == KDF_SCRYPT_KEYMASTER_BADLY_PADDED ||
+ ftr->kdf_type == KDF_SCRYPT_KEYMASTER) {
+ *kdf = scrypt_keymaster;
+ *kdf_params = ftr;
+ } else if (ftr->kdf_type == KDF_SCRYPT) {
+ *kdf = scrypt;
+ *kdf_params = ftr;
+ } else {
+ *kdf = pbkdf2;
+ *kdf_params = NULL;
+ }
+}
+
+static int decrypt_master_key(char *passwd, unsigned char *decrypted_master_key,
+ struct crypt_mnt_ftr *crypt_ftr,
+ unsigned char** intermediate_key,
+ size_t* intermediate_key_size)
+{
+ kdf_func kdf;
+ void *kdf_params;
+ int ret;
+
+ get_kdf_func(crypt_ftr, &kdf, &kdf_params);
+ ret = decrypt_master_key_aux(passwd, crypt_ftr->salt, crypt_ftr->master_key,
+ decrypted_master_key, kdf, kdf_params,
+ intermediate_key, intermediate_key_size);
+ if (ret != 0) {
+ printf("failure decrypting master key\n");
+ }
+
+ return ret;
+}
+
+static int try_mount_multiple_fs(const char *crypto_blkdev,
+ const char *mount_point,
+ const char *file_system)
+{
+ if (!mount(crypto_blkdev, mount_point, file_system, 0, NULL))
+ return 0;
+ if (strcmp(file_system, "ext4") &&
+ !mount(crypto_blkdev, mount_point, "ext4", 0, NULL))
+ return 0;
+ if (strcmp(file_system, "f2fs") &&
+ !mount(crypto_blkdev, mount_point, "f2fs", 0, NULL))
+ return 0;
+ return 1;
+}
+
+static int test_mount_encrypted_fs(struct crypt_mnt_ftr* crypt_ftr,
+ char *passwd, char *mount_point, char *label)
+{
+ /* Allocate enough space for a 256 bit key, but we may use less */
+ unsigned char decrypted_master_key[32];
+ char crypto_blkdev[MAXPATHLEN];
+ char tmp_mount_point[64];
+ int rc = 0;
+ kdf_func kdf;
+ void *kdf_params;
+ int use_keymaster = 0;
+ int upgrade = 0;
+ unsigned char* intermediate_key = 0;
+ size_t intermediate_key_size = 0;
+
+ printf("crypt_ftr->fs_size = %lld\n", crypt_ftr->fs_size);
+
+ if (! (crypt_ftr->flags & CRYPT_MNT_KEY_UNENCRYPTED) ) {
+ if (decrypt_master_key(passwd, decrypted_master_key, crypt_ftr,
+ &intermediate_key, &intermediate_key_size)) {
+ printf("Failed to decrypt master key\n");
+ rc = -1;
+ goto errout;
+ }
+ }
+
+#ifdef CONFIG_HW_DISK_ENCRYPTION
+ int key_index = 0;
+ if(is_hw_disk_encryption((char*)crypt_ftr->crypto_type_name)) {
+ key_index = verify_hw_fde_passwd(passwd, crypt_ftr);
+
+ if (key_index < 0) {
+ rc = 1;
+ goto errout;
+ }
+ else {
+ if (is_ice_enabled()) {
+ if (create_crypto_blk_dev(crypt_ftr, (unsigned char*)&key_index,
+ real_blkdev, crypto_blkdev, label)) {
+ printf("Error creating decrypted block device");
+ rc = -1;
+ goto errout;
+ }
+ } else {
+ if (create_crypto_blk_dev(crypt_ftr, decrypted_master_key,
+ real_blkdev, crypto_blkdev, label)) {
+ printf("Error creating decrypted block device");
+ rc = -1;
+ goto errout;
+ }
+ }
+ }
+ } else {
+ /* in case HW FDE is delivered through OTA and device is already encrypted
+ * using SW FDE, we should let user continue using SW FDE until userdata is
+ * wiped.
+ */
+ if (create_crypto_blk_dev(crypt_ftr, decrypted_master_key,
+ real_blkdev, crypto_blkdev, label)) {
+ printf("Error creating decrypted block device");
+ rc = -1;
+ goto errout;
+ }
+ }
+#else
+ // Create crypto block device - all (non fatal) code paths
+ // need it
+ if (create_crypto_blk_dev(crypt_ftr, decrypted_master_key,
+ real_blkdev, crypto_blkdev, label)) {
+ printf("Error creating decrypted block device\n");
+ rc = -1;
+ goto errout;
+ }
+#endif
+
+ /* Work out if the problem is the password or the data */
+ unsigned char scrypted_intermediate_key[sizeof(crypt_ftr->
+ scrypted_intermediate_key)];
+ int N = 1 << crypt_ftr->N_factor;
+ int r = 1 << crypt_ftr->r_factor;
+ int p = 1 << crypt_ftr->p_factor;
+
+ rc = crypto_scrypt(intermediate_key, intermediate_key_size,
+ crypt_ftr->salt, sizeof(crypt_ftr->salt),
+ N, r, p, scrypted_intermediate_key,
+ sizeof(scrypted_intermediate_key));
+
+ // Does the key match the crypto footer?
+ if (rc == 0 && memcmp(scrypted_intermediate_key,
+ crypt_ftr->scrypted_intermediate_key,
+ sizeof(scrypted_intermediate_key)) == 0) {
+ printf("Password matches\n");
+ rc = 0;
+ } else {
+ /* Try mounting the file system anyway, just in case the problem's with
+ * the footer, not the key. */
+ sprintf(tmp_mount_point, "%s/tmp_mnt", mount_point);
+ mkdir(tmp_mount_point, 0755);
+ if (try_mount_multiple_fs(crypto_blkdev, tmp_mount_point, file_system)) {
+ printf("Error temp mounting decrypted block device '%s'\n", crypto_blkdev);
+ delete_crypto_blk_dev(label);
+ rc = 1;
+ } else {
+ /* Success! */
+ printf("Password did not match but decrypted drive mounted - continue\n");
+ umount(tmp_mount_point);
+ rc = 0;
+ }
+ }
+
+ if (rc == 0) {
+ // Don't increment the failed attempt counter as it doesn't
+ // make sense to do so in TWRP
+
+ /* Save the name of the crypto block device
+ * so we can mount it when restarting the framework. */
+ property_set("ro.crypto.fs_crypto_blkdev", crypto_blkdev);
+
+ // TWRP shouldn't change the stored key
+ }
+
+ errout:
+ if (intermediate_key) {
+ memset(intermediate_key, 0, intermediate_key_size);
+ free(intermediate_key);
+ }
+ return rc;
+}
+
+int check_unmounted_and_get_ftr(struct crypt_mnt_ftr* crypt_ftr)
+{
+ char encrypted_state[PROPERTY_VALUE_MAX];
+ property_get("ro.crypto.state", encrypted_state, "");
+ if ( master_key_saved || strcmp(encrypted_state, "encrypted") ) {
+ printf("encrypted fs already validated or not running with encryption,"
+ " aborting\n");
+ //return -1;
+ }
+
+ if (get_crypt_ftr_and_key(crypt_ftr)) {
+ printf("Error getting crypt footer and key\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+int cryptfs_check_footer()
+{
+ int rc = -1;
+ struct crypt_mnt_ftr crypt_ftr;
+
+ rc = get_crypt_ftr_and_key(&crypt_ftr);
+
+ return rc;
+}
+
+int cryptfs_check_passwd(char *passwd)
+{
+ struct crypt_mnt_ftr crypt_ftr;
+ int rc;
+
+ if (!passwd) {
+ printf("cryptfs_check_passwd: passwd is NULL!\n");
+ return -1;
+ }
+
+ rc = check_unmounted_and_get_ftr(&crypt_ftr);
+ if (rc)
+ return rc;
+
+ rc = test_mount_encrypted_fs(&crypt_ftr, passwd,
+ DATA_MNT_POINT, "userdata");
+
+ // try falling back to Lollipop hex passwords
+ if (rc) {
+ int hex_pass_len = strlen(passwd) * 2 + 1;
+ char *hex_passwd = (char *)malloc(hex_pass_len);
+ if (hex_passwd) {
+ convert_key_to_hex_ascii((unsigned char *)passwd,
+ strlen(passwd), hex_passwd);
+ rc = test_mount_encrypted_fs(&crypt_ftr, hex_passwd,
+ DATA_MNT_POINT, "userdata");
+ memset(hex_passwd, 0, hex_pass_len);
+ free(hex_passwd);
+ }
+ }
+
+ return rc;
+}
+
+/* Returns type of the password, default, pattern, pin or password.
+ */
+int cryptfs_get_password_type(void)
+{
+ struct crypt_mnt_ftr crypt_ftr;
+
+ if (get_crypt_ftr_and_key(&crypt_ftr)) {
+ printf("Error getting crypt footer and key\n");
+ return -1;
+ }
+
+ if (crypt_ftr.flags & CRYPT_INCONSISTENT_STATE) {
+ return -1;
+ }
+
+ return crypt_ftr.crypt_type;
+}
+
+/*
+ * Called by vold when it's asked to mount an encrypted external
+ * storage volume. The incoming partition has no crypto header/footer,
+ * as any metadata is been stored in a separate, small partition.
+ *
+ * out_crypto_blkdev must be MAXPATHLEN.
+ */
+int cryptfs_setup_ext_volume(const char* label, const char* real_blkdev,
+ const unsigned char* key, int keysize, char* out_crypto_blkdev) {
+ int fd = open(real_blkdev, O_RDONLY|O_CLOEXEC);
+ if (fd == -1) {
+ printf("Failed to open %s: %s", real_blkdev, strerror(errno));
+ return -1;
+ }
+
+ unsigned long nr_sec = 0;
+ nr_sec = get_blkdev_size(fd);
+ close(fd);
+
+ if (nr_sec == 0) {
+ printf("Failed to get size of %s: %s", real_blkdev, strerror(errno));
+ return -1;
+ }
+
+ struct crypt_mnt_ftr ext_crypt_ftr;
+ memset(&ext_crypt_ftr, 0, sizeof(ext_crypt_ftr));
+ ext_crypt_ftr.fs_size = nr_sec;
+ ext_crypt_ftr.keysize = keysize;
+ strcpy((char*) ext_crypt_ftr.crypto_type_name, "aes-cbc-essiv:sha256");
+
+ return create_crypto_blk_dev(&ext_crypt_ftr, key, real_blkdev,
+ out_crypto_blkdev, label);
+}
+
+/*
+ * Called by vold when it's asked to unmount an encrypted external
+ * storage volume.
+ */
+int cryptfs_revert_ext_volume(const char* label) {
+ return delete_crypto_blk_dev((char*) label);
+}
diff --git a/crypto/lollipop/cryptfs.h b/crypto/lollipop/cryptfs.h
new file mode 100644
index 000000000..cd07e5af7
--- /dev/null
+++ b/crypto/lollipop/cryptfs.h
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+/* This structure starts 16,384 bytes before the end of a hardware
+ * partition that is encrypted, or in a separate partition. It's location
+ * is specified by a property set in init.<device>.rc.
+ * The structure allocates 48 bytes for a key, but the real key size is
+ * specified in the struct. Currently, the code is hardcoded to use 128
+ * bit keys.
+ * The fields after salt are only valid in rev 1.1 and later stuctures.
+ * Obviously, the filesystem does not include the last 16 kbytes
+ * of the partition if the crypt_mnt_ftr lives at the end of the
+ * partition.
+ */
+
+#include <cutils/properties.h>
+#include "openssl/sha.h"
+
+/* The current cryptfs version */
+#define CURRENT_MAJOR_VERSION 1
+#define CURRENT_MINOR_VERSION 3
+
+#define CRYPT_FOOTER_OFFSET 0x4000
+#define CRYPT_FOOTER_TO_PERSIST_OFFSET 0x1000
+#define CRYPT_PERSIST_DATA_SIZE 0x1000
+
+#define MAX_CRYPTO_TYPE_NAME_LEN 64
+
+#define MAX_KEY_LEN 48
+#define SALT_LEN 16
+#define SCRYPT_LEN 32
+
+/* definitions of flags in the structure below */
+#define CRYPT_MNT_KEY_UNENCRYPTED 0x1 /* The key for the partition is not encrypted. */
+#define CRYPT_ENCRYPTION_IN_PROGRESS 0x2 /* Encryption partially completed,
+ encrypted_upto valid*/
+#define CRYPT_INCONSISTENT_STATE 0x4 /* Set when starting encryption, clear when
+ exit cleanly, either through success or
+ correctly marked partial encryption */
+#define CRYPT_DATA_CORRUPT 0x8 /* Set when encryption is fine, but the
+ underlying volume is corrupt */
+#ifdef CONFIG_HW_DISK_ENCRYPTION
+/* This flag is used to transition from L->M upgrade. L release passed
+ * a byte for every nible of user password while M release is passing
+ * ascii value of user password.
+ * Random flag value is chosen so that it does not conflict with other use cases
+ */
+#define CRYPT_ASCII_PASSWORD_UPDATED 0x1000
+#endif
+/* Allowed values for type in the structure below */
+#define CRYPT_TYPE_PASSWORD 0 /* master_key is encrypted with a password
+ * Must be zero to be compatible with pre-L
+ * devices where type is always password.*/
+#define CRYPT_TYPE_DEFAULT 1 /* master_key is encrypted with default
+ * password */
+#define CRYPT_TYPE_PATTERN 2 /* master_key is encrypted with a pattern */
+#define CRYPT_TYPE_PIN 3 /* master_key is encrypted with a pin */
+#define CRYPT_TYPE_MAX_TYPE 3 /* type cannot be larger than this value */
+
+#define CRYPT_MNT_MAGIC 0xD0B5B1C4
+#define PERSIST_DATA_MAGIC 0xE950CD44
+
+#define SCRYPT_PROP "ro.crypto.scrypt_params"
+#define SCRYPT_DEFAULTS { 15, 3, 1 }
+
+/* Key Derivation Function algorithms */
+#define KDF_PBKDF2 1
+#define KDF_SCRYPT 2
+/* TODO(paullawrence): Remove KDF_SCRYPT_KEYMASTER_UNPADDED and KDF_SCRYPT_KEYMASTER_BADLY_PADDED
+ * when it is safe to do so. */
+#define KDF_SCRYPT_KEYMASTER_UNPADDED 3
+#define KDF_SCRYPT_KEYMASTER_BADLY_PADDED 4
+#define KDF_SCRYPT_KEYMASTER 5
+
+/* Maximum allowed keymaster blob size. */
+#define KEYMASTER_BLOB_SIZE 2048
+
+/* __le32 and __le16 defined in system/extras/ext4_utils/ext4_utils.h */
+#define __le8 unsigned char
+
+struct crypt_mnt_ftr {
+ __le32 magic; /* See above */
+ __le16 major_version;
+ __le16 minor_version;
+ __le32 ftr_size; /* in bytes, not including key following */
+ __le32 flags; /* See above */
+ __le32 keysize; /* in bytes */
+ __le32 crypt_type; /* how master_key is encrypted. Must be a
+ * CRYPT_TYPE_XXX value */
+ __le64 fs_size; /* Size of the encrypted fs, in 512 byte sectors */
+ __le32 failed_decrypt_count; /* count of # of failed attempts to decrypt and
+ mount, set to 0 on successful mount */
+ unsigned char crypto_type_name[MAX_CRYPTO_TYPE_NAME_LEN]; /* The type of encryption
+ needed to decrypt this
+ partition, null terminated */
+ __le32 spare2; /* ignored */
+ unsigned char master_key[MAX_KEY_LEN]; /* The encrypted key for decrypting the filesystem */
+ unsigned char salt[SALT_LEN]; /* The salt used for this encryption */
+ __le64 persist_data_offset[2]; /* Absolute offset to both copies of crypt_persist_data
+ * on device with that info, either the footer of the
+ * real_blkdevice or the metadata partition. */
+
+ __le32 persist_data_size; /* The number of bytes allocated to each copy of the
+ * persistent data table*/
+
+ __le8 kdf_type; /* The key derivation function used. */
+
+ /* scrypt parameters. See www.tarsnap.com/scrypt/scrypt.pdf */
+ __le8 N_factor; /* (1 << N) */
+ __le8 r_factor; /* (1 << r) */
+ __le8 p_factor; /* (1 << p) */
+ __le64 encrypted_upto; /* If we are in state CRYPT_ENCRYPTION_IN_PROGRESS and
+ we have to stop (e.g. power low) this is the last
+ encrypted 512 byte sector.*/
+ __le8 hash_first_block[SHA256_DIGEST_LENGTH]; /* When CRYPT_ENCRYPTION_IN_PROGRESS
+ set, hash of first block, used
+ to validate before continuing*/
+
+ /* key_master key, used to sign the derived key which is then used to generate
+ * the intermediate key
+ * This key should be used for no other purposes! We use this key to sign unpadded
+ * data, which is acceptable but only if the key is not reused elsewhere. */
+ __le8 keymaster_blob[KEYMASTER_BLOB_SIZE];
+ __le32 keymaster_blob_size;
+
+ /* Store scrypt of salted intermediate key. When decryption fails, we can
+ check if this matches, and if it does, we know that the problem is with the
+ drive, and there is no point in asking the user for more passwords.
+
+ Note that if any part of this structure is corrupt, this will not match and
+ we will continue to believe the user entered the wrong password. In that
+ case the only solution is for the user to enter a password enough times to
+ force a wipe.
+
+ Note also that there is no need to worry about migration. If this data is
+ wrong, we simply won't recognise a right password, and will continue to
+ prompt. On the first password change, this value will be populated and
+ then we will be OK.
+ */
+ unsigned char scrypted_intermediate_key[SCRYPT_LEN];
+};
+
+/* Persistant data that should be available before decryption.
+ * Things like airplane mode, locale and timezone are kept
+ * here and can be retrieved by the CryptKeeper UI to properly
+ * configure the phone before asking for the password
+ * This is only valid if the major and minor version above
+ * is set to 1.1 or higher.
+ *
+ * This is a 4K structure. There are 2 copies, and the code alternates
+ * writing one and then clearing the previous one. The reading
+ * code reads the first valid copy it finds, based on the magic number.
+ * The absolute offset to the first of the two copies is kept in rev 1.1
+ * and higher crypt_mnt_ftr structures.
+ */
+struct crypt_persist_entry {
+ char key[PROPERTY_KEY_MAX];
+ char val[PROPERTY_VALUE_MAX];
+};
+
+/* Should be exactly 4K in size */
+struct crypt_persist_data {
+ __le32 persist_magic;
+ __le32 persist_valid_entries;
+ __le32 persist_spare[30];
+ struct crypt_persist_entry persist_entry[0];
+};
+
+struct volume_info {
+ unsigned int size;
+ unsigned int flags;
+ struct crypt_mnt_ftr crypt_ftr;
+ char mnt_point[256];
+ char blk_dev[256];
+ char crypto_blkdev[256];
+ char label[256];
+};
+#define VOL_NONREMOVABLE 0x1
+#define VOL_ENCRYPTABLE 0x2
+#define VOL_PRIMARY 0x4
+#define VOL_PROVIDES_ASEC 0x8
+
+#define DATA_MNT_POINT "/data"
+
+/* Return values for cryptfs_crypto_complete */
+#define CRYPTO_COMPLETE_NOT_ENCRYPTED 1
+#define CRYPTO_COMPLETE_ENCRYPTED 0
+#define CRYPTO_COMPLETE_BAD_METADATA -1
+#define CRYPTO_COMPLETE_PARTIAL -2
+#define CRYPTO_COMPLETE_INCONSISTENT -3
+#define CRYPTO_COMPLETE_CORRUPT -4
+
+/* Return values for cryptfs_enable_inplace*() */
+#define ENABLE_INPLACE_OK 0
+#define ENABLE_INPLACE_ERR_OTHER -1
+#define ENABLE_INPLACE_ERR_DEV -2 /* crypto_blkdev issue */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ typedef int (*kdf_func)(const char *passwd, const unsigned char *salt,
+ unsigned char *ikey, void *params);
+
+ void set_partition_data(const char* block_device, const char* key_location, const char* fs);
+ int cryptfs_check_footer();
+ int cryptfs_check_passwd(char *pw);
+ int cryptfs_verify_passwd(char *newpw);
+ int cryptfs_get_password_type(void);
+ int delete_crypto_blk_dev(char *name);
+ int cryptfs_setup_ext_volume(const char* label, const char* real_blkdev,
+ const unsigned char* key, int keysize, char* out_crypto_blkdev);
+ int cryptfs_revert_ext_volume(const char* label);
+#ifdef __cplusplus
+}
+#endif
diff --git a/crypto/lollipop/main.c b/crypto/lollipop/main.c
new file mode 100644
index 000000000..232afb959
--- /dev/null
+++ b/crypto/lollipop/main.c
@@ -0,0 +1,32 @@
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <linux/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <linux/dm-ioctl.h>
+#include <libgen.h>
+#include <stdlib.h>
+#include <sys/param.h>
+#include <string.h>
+#include <sys/mount.h>
+#include <openssl/evp.h>
+#include <errno.h>
+#include <linux/kdev_t.h>
+#include <time.h>
+#include "cryptfs.h"
+#include "cutils/properties.h"
+#include "crypto_scrypt.h"
+
+int main() {
+ set_partition_data("/dev/block/platform/sdhci-tegra.3/by-name/UDA", "/dev/block/platform/sdhci-tegra.3/by-name/MD1", "f2fs");
+ //int ret = cryptfs_check_passwd("30303030");
+ int ret = cryptfs_check_passwd("0000");
+ return 0;
+}
diff --git a/crypto/scrypt/Android.mk b/crypto/scrypt/Android.mk
new file mode 100644
index 000000000..4514f9467
--- /dev/null
+++ b/crypto/scrypt/Android.mk
@@ -0,0 +1,13 @@
+LOCAL_PATH := $(call my-dir)
+
+# Enable to be able to use ALOG* with #include "cutils/log.h"
+#log_c_includes += system/core/include
+#log_shared_libraries := liblog
+
+# These makefiles are here instead of being Android.mk files in the
+# respective crypto, ssl, and apps directories so
+# that import_openssl.sh import won't remove them.
+include $(LOCAL_PATH)/build-config.mk
+include $(LOCAL_PATH)/Scrypt.mk
+
+include $(LOCAL_PATH)/tests/Android.mk
diff --git a/crypto/scrypt/MODULE_LICENSE_BSD_LIKE b/crypto/scrypt/MODULE_LICENSE_BSD_LIKE
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/crypto/scrypt/MODULE_LICENSE_BSD_LIKE
diff --git a/crypto/scrypt/NOTICE b/crypto/scrypt/NOTICE
new file mode 100644
index 000000000..b0b9311e6
--- /dev/null
+++ b/crypto/scrypt/NOTICE
@@ -0,0 +1,36 @@
+/*-
+ * Copyright 2009 Colin Percival
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This file was originally written by Colin Percival as part of the Tarsnap
+ * online backup system.
+ */
+
+/*
+ * version 20110505
+ * D. J. Bernstein
+ * Public domain.
+ *
+ * Based on crypto_core/salsa208/armneon/core.c from SUPERCOP 20130419
+ */
diff --git a/crypto/scrypt/Scrypt-config.mk b/crypto/scrypt/Scrypt-config.mk
new file mode 100644
index 000000000..bbe10631e
--- /dev/null
+++ b/crypto/scrypt/Scrypt-config.mk
@@ -0,0 +1,105 @@
+# Auto-generated - DO NOT EDIT!
+# To regenerate, edit scrypt.config, then run:
+# ./import_scrypt.sh import /path/to/scrypt-1.1.6.tar.gz
+#
+# Before including this file, the local Android.mk must define the following
+# variables:
+#
+# local_c_flags
+# local_c_includes
+# local_additional_dependencies
+#
+# This script will define the following variables:
+#
+# target_c_flags
+# target_c_includes
+# target_src_files
+#
+# host_c_flags
+# host_c_includes
+# host_src_files
+#
+
+# Ensure these are empty.
+unknown_arch_c_flags :=
+unknown_arch_src_files :=
+unknown_arch_exclude_files :=
+
+
+common_c_flags :=
+
+common_src_files := \
+ lib/crypto/crypto_scrypt-ref.c \
+
+common_c_includes := \
+ lib/crypto \
+ lib/util \
+
+arm_c_flags :=
+
+arm_src_files :=
+
+arm_exclude_files :=
+
+arm_neon_c_flags :=
+
+arm_neon_src_files := \
+ lib/crypto/crypto_scrypt-neon.c \
+
+arm_neon_exclude_files := \
+ lib/crypto/crypto_scrypt-ref.c \
+
+x86_c_flags :=
+
+x86_src_files := \
+ lib/crypto/crypto_scrypt-sse.c \
+
+x86_exclude_files := \
+ lib/crypto/crypto_scrypt-ref.c \
+
+x86_64_c_flags :=
+
+x86_64_src_files := \
+ lib/crypto/crypto_scrypt-sse.c \
+
+x86_64_exclude_files := \
+ lib/crypto/crypto_scrypt-ref.c \
+
+mips_c_flags :=
+
+mips_src_files :=
+
+mips_exclude_files :=
+
+target_arch := $(TARGET_ARCH)
+ifeq ($(target_arch)-$(TARGET_HAS_BIGENDIAN),mips-true)
+target_arch := unknown_arch
+endif
+
+target_c_flags := $(common_c_flags) $($(target_arch)_c_flags) $(local_c_flags)
+target_c_includes := $(addprefix external/scrypt/,$(common_c_includes)) $(local_c_includes)
+target_src_files := $(common_src_files) $($(target_arch)_src_files)
+target_src_files := $(filter-out $($(target_arch)_exclude_files), $(target_src_files))
+
+# Hacks for ARM NEON support
+ifeq ($(target_arch),arm)
+ifeq ($(ARCH_ARM_HAVE_NEON),true)
+target_c_flags += $(arm_neon_c_flags)
+target_src_files += $(arm_neon_src_files)
+target_src_files := $(filter-out $(arm_neon_exclude_files), $(target_src_files))
+endif
+endif
+
+ifeq ($(HOST_OS)-$(HOST_ARCH),linux-x86)
+host_arch := x86
+else
+host_arch := unknown_arch
+endif
+
+host_c_flags := $(common_c_flags) $($(host_arch)_c_flags) $(local_c_flags)
+host_c_includes := $(addprefix external/scrypt/,$(common_c_includes)) $(local_c_includes)
+host_src_files := $(common_src_files) $($(host_arch)_src_files)
+host_src_files := $(filter-out $($(host_arch)_exclude_files), $(host_src_files))
+
+local_additional_dependencies += $(LOCAL_PATH)/Scrypt-config.mk
+
diff --git a/crypto/scrypt/Scrypt.mk b/crypto/scrypt/Scrypt.mk
new file mode 100644
index 000000000..baa41eca6
--- /dev/null
+++ b/crypto/scrypt/Scrypt.mk
@@ -0,0 +1,46 @@
+local_c_flags := -DUSE_OPENSSL_PBKDF2
+
+local_c_includes := $(log_c_includes) external/openssl/include external/boringssl/src/include
+
+local_additional_dependencies := $(LOCAL_PATH)/android-config.mk $(LOCAL_PATH)/Scrypt.mk
+
+include $(LOCAL_PATH)/Scrypt-config.mk
+
+#######################################
+# target static library
+include $(CLEAR_VARS)
+include $(LOCAL_PATH)/android-config.mk
+
+LOCAL_SHARED_LIBRARIES := $(log_shared_libraries)
+
+# If we're building an unbundled build, don't try to use clang since it's not
+# in the NDK yet. This can be removed when a clang version that is fast enough
+# in the NDK.
+ifeq (,$(TARGET_BUILD_APPS))
+LOCAL_CLANG := true
+else
+LOCAL_SDK_VERSION := 9
+endif
+
+LOCAL_SRC_FILES += $(target_src_files)
+LOCAL_CFLAGS += $(target_c_flags)
+LOCAL_C_INCLUDES += $(target_c_includes) $(commands_recovery_local_path)/crypto/scrypt/lib/util
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE:= libscrypttwrp_static
+LOCAL_ADDITIONAL_DEPENDENCIES := $(local_additional_dependencies)
+include $(BUILD_STATIC_LIBRARY)
+
+########################################
+# host static library
+
+include $(CLEAR_VARS)
+include $(LOCAL_PATH)/android-config.mk
+LOCAL_SHARED_LIBRARIES := $(log_shared_libraries)
+LOCAL_SRC_FILES += $(host_src_files)
+LOCAL_CFLAGS += $(host_c_flags)
+LOCAL_C_INCLUDES += $(host_c_includes) $(commands_recovery_local_path)/crypto/scrypt/lib/util
+LOCAL_LDLIBS += -ldl
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE:= libscrypttwrp_static
+LOCAL_ADDITIONAL_DEPENDENCIES := $(local_additional_dependencies)
+include $(BUILD_HOST_STATIC_LIBRARY)
diff --git a/crypto/scrypt/android-config.mk b/crypto/scrypt/android-config.mk
new file mode 100644
index 000000000..326e1134e
--- /dev/null
+++ b/crypto/scrypt/android-config.mk
@@ -0,0 +1,16 @@
+#
+# These flags represent the build-time configuration of scrypt for Android
+#
+# The value of $(scrypt_cflags) was pruned from the Makefile generated
+# by running ./configure from import_scrypt.sh.
+#
+# This script performs minor but required patching for the Android build.
+#
+
+LOCAL_CFLAGS += $(scrypt_cflags)
+
+# Add in flags to let config.h be read properly
+LOCAL_CFLAGS += "-DHAVE_CONFIG_H"
+
+# Add clang here when it works on host
+# LOCAL_CLANG := true
diff --git a/crypto/scrypt/build-config.mk b/crypto/scrypt/build-config.mk
new file mode 100644
index 000000000..3d2ab9195
--- /dev/null
+++ b/crypto/scrypt/build-config.mk
@@ -0,0 +1,6 @@
+# Auto-generated - DO NOT EDIT!
+# To regenerate, edit scrypt.config, then run:
+# ./import_scrypt.sh import /path/to/scrypt-1.1.6.tar.gz
+#
+scrypt_cflags := \
+
diff --git a/crypto/scrypt/config.h b/crypto/scrypt/config.h
new file mode 100644
index 000000000..3514f398e
--- /dev/null
+++ b/crypto/scrypt/config.h
@@ -0,0 +1,99 @@
+/* config.h. Generated from config.h.in by configure. */
+/* config.h.in. Generated from configure.ac by autoheader. */
+
+/* Define to 1 if you have the `clock_gettime' function. */
+#define HAVE_CLOCK_GETTIME 1
+
+/* Define to 1 if you have the declaration of `be64enc', and to 0 if you
+ don't. */
+#define HAVE_DECL_BE64ENC 0
+
+/* Define to 1 if you have the <err.h> header file. */
+#define HAVE_ERR_H 1
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have the `rt' library (-lrt). */
+#define HAVE_LIBRT 1
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define to 1 if you have the `posix_memalign' function. */
+#define HAVE_POSIX_MEMALIGN 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if the system has the type `struct sysinfo'. */
+#define HAVE_STRUCT_SYSINFO 1
+
+/* Define to 1 if `mem_unit' is member of `struct sysinfo'. */
+#define HAVE_STRUCT_SYSINFO_MEM_UNIT 1
+
+/* Define to 1 if `totalram' is member of `struct sysinfo'. */
+#define HAVE_STRUCT_SYSINFO_TOTALRAM 1
+
+/* Define to 1 if the OS has a hw.usermem sysctl */
+/* #undef HAVE_SYSCTL_HW_USERMEM */
+
+/* Define to 1 if you have the `sysinfo' function. */
+#define HAVE_SYSINFO 1
+
+/* Define to 1 if you have the <sys/endian.h> header file. */
+/* #undef HAVE_SYS_ENDIAN_H */
+
+/* Define to 1 if you have the <sys/param.h> header file. */
+#define HAVE_SYS_PARAM_H 1
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/sysinfo.h> header file. */
+#define HAVE_SYS_SYSINFO_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Name of package */
+#define PACKAGE "scrypt"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT ""
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "scrypt"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "scrypt 1.1.6"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "scrypt"
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "1.1.6"
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Version number of package */
+#define VERSION "1.1.6"
+
+/* Number of bits in a file offset, on hosts where this is settable. */
+/* #undef _FILE_OFFSET_BITS */
+
+/* Define for large files, on AIX-style hosts. */
+/* #undef _LARGE_FILES */
diff --git a/crypto/scrypt/import_scrypt.sh b/crypto/scrypt/import_scrypt.sh
new file mode 100755
index 000000000..324eae64b
--- /dev/null
+++ b/crypto/scrypt/import_scrypt.sh
@@ -0,0 +1,493 @@
+#!/bin/bash
+#
+# Copyright (C) 2009 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.
+#
+
+#
+# This script imports new versions of scrypt (http://www.tarsnap.com/scrypt/) into the
+# Android source tree. To run, (1) fetch the appropriate tarball from the scrypt repository,
+# (2) check the gpg/pgp signature, and then (3) run:
+# ./import_scrypt.sh import scrypt-*.tar.gz
+#
+# IMPORTANT: See README.android for additional details.
+
+# turn on exit on error as well as a warning when it happens
+set -e
+set -x
+trap "echo WARNING: Exiting on non-zero subprocess exit code" ERR;
+
+# Ensure consistent sorting order / tool output.
+export LANG=C
+export LC_ALL=C
+
+export DIRNAME=$(dirname $0)
+
+function die() {
+ declare -r message=$1
+
+ echo $message
+ exit 1
+}
+
+function usage() {
+ declare -r message=$1
+
+ if [ ! "$message" = "" ]; then
+ echo $message
+ fi
+ echo "Usage:"
+ echo " ./import_scrypt.sh import </path/to/scrypt-*.tar.gz>"
+ echo " ./import_scrypt.sh regenerate <patch/*.patch>"
+ echo " ./import_scrypt.sh generate <patch/*.patch> </path/to/scrypt-*.tar.gz>"
+ exit 1
+}
+
+function main() {
+ if [ ! -d patches ]; then
+ die "scrypt patch directory patches/ not found"
+ fi
+
+ if [ ! -f scrypt.version ]; then
+ die "scrypt.version not found"
+ fi
+
+ source $DIRNAME/scrypt.version
+ if [ "$SCRYPT_VERSION" == "" ]; then
+ die "Invalid scrypt.version; see README.android for more information"
+ fi
+
+ SCRYPT_DIR=scrypt-$SCRYPT_VERSION
+ SCRYPT_DIR_ORIG=$SCRYPT_DIR.orig
+
+ if [ ! -f scrypt.config ]; then
+ die "scrypt.config not found"
+ fi
+
+ source $DIRNAME/scrypt.config
+ if [ "$CONFIGURE_ARGS" == "" -o "$UNNEEDED_SOURCES" == "" -o "$NEEDED_SOURCES" == "" ]; then
+ die "Invalid scrypt.config; see README.android for more information"
+ fi
+
+ declare -r command=$1
+ shift || usage "No command specified. Try import, regenerate, or generate."
+ if [ "$command" = "import" ]; then
+ declare -r tar=$1
+ shift || usage "No tar file specified."
+ import $tar
+ elif [ "$command" = "regenerate" ]; then
+ declare -r patch=$1
+ shift || usage "No patch file specified."
+ [ -d $SCRYPT_DIR ] || usage "$SCRYPT_DIR not found, did you mean to use generate?"
+ [ -d $SCRYPT_DIR_ORIG_ORIG ] || usage "$SCRYPT_DIR_ORIG not found, did you mean to use generate?"
+ regenerate $patch
+ elif [ "$command" = "generate" ]; then
+ declare -r patch=$1
+ shift || usage "No patch file specified."
+ declare -r tar=$1
+ shift || usage "No tar file specified."
+ generate $patch $tar
+ else
+ usage "Unknown command specified $command. Try import, regenerate, or generate."
+ fi
+}
+
+# Compute the name of an assembly source file generated by one of the
+# gen_asm_xxxx() functions below. The logic is the following:
+# - if "$2" is not empty, output it directly
+# - otherwise, change the file extension of $1 from .pl to .S and output
+# it.
+# Usage: default_asm_file "$1" "$2"
+# or default_asm_file "$@"
+#
+# $1: generator path (perl script)
+# $2: optional output file name.
+function default_asm_file () {
+ if [ "$2" ]; then
+ echo "$2"
+ else
+ echo "${1%%.pl}.S"
+ fi
+}
+
+# Generate an ARM assembly file.
+# $1: generator (perl script)
+# $2: [optional] output file name
+function gen_asm_arm () {
+ local OUT
+ OUT=$(default_asm_file "$@")
+ perl "$1" > "$OUT"
+}
+
+function gen_asm_mips () {
+ local OUT
+ OUT=$(default_asm_file "$@")
+ # The perl scripts expect to run the target compiler as $CC to determine
+ # the endianess of the target. Setting CC to true is a hack that forces the scripts
+ # to generate little endian output
+ CC=true perl "$1" o32 > "$OUT"
+}
+
+function gen_asm_x86 () {
+ local OUT
+ OUT=$(default_asm_file "$@")
+ perl "$1" elf -fPIC > "$OUT"
+}
+
+function gen_asm_x86_64 () {
+ local OUT
+ OUT=$(default_asm_file "$@")
+ perl "$1" elf "$OUT" > "$OUT"
+}
+
+
+# Filter all items in a list that match a given pattern.
+# $1: space-separated list
+# $2: egrep pattern.
+# Out: items in $1 that match $2
+function filter_by_egrep() {
+ declare -r pattern=$1
+ shift
+ echo "$@" | tr ' ' '\n' | grep -e "$pattern" | tr '\n' ' '
+}
+
+# Sort and remove duplicates in a space-separated list
+# $1: space-separated list
+# Out: new space-separated list
+function uniq_sort () {
+ echo "$@" | tr ' ' '\n' | sort -u | tr '\n' ' '
+}
+
+function print_autogenerated_header() {
+ echo "# Auto-generated - DO NOT EDIT!"
+ echo "# To regenerate, edit scrypt.config, then run:"
+ echo "# ./import_scrypt.sh import /path/to/scrypt-$SCRYPT_VERSION.tar.gz"
+ echo "#"
+}
+
+function generate_build_config_mk() {
+ ./configure $CONFIGURE_ARGS
+ #rm -f apps/CA.pl.bak crypto/scryptconf.h.bak
+
+ declare -r tmpfile=$(mktemp)
+ (grep -e -D Makefile | grep -v CONFIGURE_ARGS= | grep -v OPTIONS=) > $tmpfile
+
+ declare -r cflags=$(filter_by_egrep "^-D" $(grep -e "^CFLAG=" $tmpfile))
+ declare -r depflags=$(filter_by_egrep "^-D" $(grep -e "^DEPFLAG=" $tmpfile))
+ rm -f $tmpfile
+
+ echo "Generating $(basename $1)"
+ (
+ print_autogenerated_header
+
+ echo "scrypt_cflags := \\"
+ for cflag in $cflags $depflags; do
+ echo " $cflag \\"
+ done
+ echo ""
+ ) > $1
+}
+
+# Return the value of a computed variable name.
+# E.g.:
+# FOO=foo
+# BAR=bar
+# echo $(var_value FOO_$BAR) -> prints the value of ${FOO_bar}
+# $1: Variable name
+# Out: variable value
+var_value() {
+ # Note: don't use 'echo' here, because it's sensitive to values
+ # that begin with an underscore (e.g. "-n")
+ eval printf \"%s\\n\" \$$1
+}
+
+# Same as var_value, but returns sorted output without duplicates.
+# $1: Variable name
+# Out: variable value (if space-separated list, sorted with no duplicates)
+var_sorted_value() {
+ uniq_sort $(var_value $1)
+}
+
+# Print the definition of a given variable in a GNU Make build file.
+# $1: Variable name (e.g. common_src_files)
+# $2+: Variable value (e.g. list of sources)
+print_vardef_in_mk() {
+ declare -r varname=$1
+ shift
+ if [ -z "$1" ]; then
+ echo "$varname :="
+ else
+ echo "$varname := \\"
+ for src; do
+ echo " $src \\"
+ done
+ fi
+ echo ""
+}
+
+# Same as print_vardef_in_mk, but print a CFLAGS definition from
+# a list of compiler defines.
+# $1: Variable name (e.g. common_c_flags)
+# $2: List of defines (e.g. SCRYPT_NO_DONKEYS ...)
+print_defines_in_mk() {
+ declare -r varname=$1
+ shift
+ if [ -z "$1" ]; then
+ echo "$varname :="
+ else
+ echo "$varname := \\"
+ for def; do
+ echo " -D$def \\"
+ done
+ fi
+ echo ""
+}
+
+# Generate a configuration file like Scrypt-config.mk
+# This uses variable definitions from scrypt.config to build a config
+# file that can compute the list of target- and host-specific sources /
+# compiler flags for a given component.
+#
+# $1: Target file name. (e.g. Scrypt-config.mk)
+function generate_config_mk() {
+ declare -r output="$1"
+ declare -r all_archs="arm arm_neon x86 x86_64 mips"
+
+ echo "Generating $(basename $output)"
+ (
+ print_autogenerated_header
+ echo \
+"# Before including this file, the local Android.mk must define the following
+# variables:
+#
+# local_c_flags
+# local_c_includes
+# local_additional_dependencies
+#
+# This script will define the following variables:
+#
+# target_c_flags
+# target_c_includes
+# target_src_files
+#
+# host_c_flags
+# host_c_includes
+# host_src_files
+#
+
+# Ensure these are empty.
+unknown_arch_c_flags :=
+unknown_arch_src_files :=
+unknown_arch_exclude_files :=
+
+"
+ common_defines=$(var_sorted_value SCRYPT_DEFINES)
+ print_defines_in_mk common_c_flags $common_defines
+
+ common_sources=$(var_sorted_value SCRYPT_SOURCES)
+ print_vardef_in_mk common_src_files $common_sources
+
+ common_includes=$(var_sorted_value SCRYPT_INCLUDES)
+ print_vardef_in_mk common_c_includes $common_includes
+
+ for arch in $all_archs; do
+ arch_defines=$(var_sorted_value SCRYPT_DEFINES_${arch})
+ print_defines_in_mk ${arch}_c_flags $arch_defines
+
+ arch_sources=$(var_sorted_value SCRYPT_SOURCES_${arch})
+ print_vardef_in_mk ${arch}_src_files $arch_sources
+
+ arch_exclude_sources=$(var_sorted_value SCRYPT_SOURCES_EXCLUDES_${arch})
+ print_vardef_in_mk ${arch}_exclude_files $arch_exclude_sources
+
+ done
+
+ echo "\
+target_arch := \$(TARGET_ARCH)
+ifeq (\$(target_arch)-\$(TARGET_HAS_BIGENDIAN),mips-true)
+target_arch := unknown_arch
+endif
+
+target_c_flags := \$(common_c_flags) \$(\$(target_arch)_c_flags) \$(local_c_flags)
+target_c_includes := \$(addprefix external/scrypt/,\$(common_c_includes)) \$(local_c_includes)
+target_src_files := \$(common_src_files) \$(\$(target_arch)_src_files)
+target_src_files := \$(filter-out \$(\$(target_arch)_exclude_files), \$(target_src_files))
+
+# Hacks for ARM NEON support
+ifeq (\$(target_arch),arm)
+ifeq (\$(ARCH_ARM_HAVE_NEON),true)
+target_c_flags += \$(arm_neon_c_flags)
+target_src_files += \$(arm_neon_src_files)
+target_src_files := \$(filter-out \$(arm_neon_exclude_files), \$(target_src_files))
+endif
+endif
+
+ifeq (\$(HOST_OS)-\$(HOST_ARCH),linux-x86)
+host_arch := x86
+else
+host_arch := unknown_arch
+endif
+
+host_c_flags := \$(common_c_flags) \$(\$(host_arch)_c_flags) \$(local_c_flags)
+host_c_includes := \$(addprefix external/scrypt/,\$(common_c_includes)) \$(local_c_includes)
+host_src_files := \$(common_src_files) \$(\$(host_arch)_src_files)
+host_src_files := \$(filter-out \$(\$(host_arch)_exclude_files), \$(host_src_files))
+
+local_additional_dependencies += \$(LOCAL_PATH)/$(basename $output)
+"
+
+ ) > "$output"
+}
+
+function import() {
+ declare -r SCRYPT_SOURCE=$1
+
+ untar $SCRYPT_SOURCE readonly
+ applypatches $SCRYPT_DIR
+
+ cd $SCRYPT_DIR
+
+ generate_build_config_mk ../build-config.mk
+
+ touch ../MODULE_LICENSE_BSD_LIKE
+
+ cd ..
+
+ generate_config_mk Scrypt-config.mk
+
+ # Prune unnecessary sources
+ prune
+
+ NEEDED_SOURCES="$NEEDED_SOURCES"
+ for i in $NEEDED_SOURCES; do
+ echo "Updating $i"
+ rm -r $i
+ mv $SCRYPT_DIR/$i .
+ done
+
+ cleantar
+}
+
+function regenerate() {
+ declare -r patch=$1
+
+ generatepatch $patch
+}
+
+function generate() {
+ declare -r patch=$1
+ declare -r SCRYPT_SOURCE=$2
+
+ untar $SCRYPT_SOURCE
+ applypatches $SCRYPT_DIR_ORIG $patch
+ prune
+
+ for i in $NEEDED_SOURCES; do
+ echo "Restoring $i"
+ rm -r $SCRYPT_DIR/$i
+ cp -rf $i $SCRYPT_DIR/$i
+ done
+
+ generatepatch $patch
+ cleantar
+}
+
+# Find all files in a sub-directory that are encoded in ISO-8859
+# $1: Directory.
+# Out: list of files in $1 that are encoded as ISO-8859.
+function find_iso8859_files() {
+ find $1 -type f -print0 | xargs -0 file | fgrep "ISO-8859" | cut -d: -f1
+}
+
+# Convert all ISO-8859 files in a given subdirectory to UTF-8
+# $1: Directory name
+function convert_iso8859_to_utf8() {
+ declare -r iso_files=$(find_iso8859_files "$1")
+ for iso_file in $iso_files; do
+ iconv --from-code iso-8859-1 --to-code utf-8 $iso_file > $iso_file.tmp
+ rm -f $iso_file
+ mv $iso_file.tmp $iso_file
+ done
+}
+
+function untar() {
+ declare -r SCRYPT_SOURCE=$1
+ declare -r readonly=$2
+
+ # Remove old source
+ cleantar
+
+ # Process new source
+ tar -zxf $SCRYPT_SOURCE
+ convert_iso8859_to_utf8 $SCRYPT_DIR
+ cp -rfP $SCRYPT_DIR $SCRYPT_DIR_ORIG
+ if [ ! -z $readonly ]; then
+ find $SCRYPT_DIR_ORIG -type f -print0 | xargs -0 chmod a-w
+ fi
+}
+
+function prune() {
+ echo "Removing $UNNEEDED_SOURCES"
+ (cd $SCRYPT_DIR_ORIG && rm -rf $UNNEEDED_SOURCES)
+ (cd $SCRYPT_DIR && rm -r $UNNEEDED_SOURCES)
+}
+
+function cleantar() {
+ rm -rf $SCRYPT_DIR_ORIG
+ rm -rf $SCRYPT_DIR
+}
+
+function applypatches () {
+ declare -r dir=$1
+ declare -r skip_patch=$2
+
+ cd $dir
+
+ # Apply appropriate patches
+ for i in $SCRYPT_PATCHES; do
+ if [ ! "$skip_patch" = "patches/$i" ]; then
+ echo "Applying patch $i"
+ patch -p1 --merge < ../patches/$i || die "Could not apply patches/$i. Fix source and run: $0 regenerate patches/$i"
+ else
+ echo "Skiping patch $i"
+ fi
+
+ done
+
+ # Cleanup patch output
+ find . \( -type f -o -type l \) -name "*.orig" -print0 | xargs -0 rm -f
+
+ cd ..
+}
+
+function generatepatch() {
+ declare -r patch=$1
+
+ # Cleanup stray files before generating patch
+ find $SCRYPT_DIR -type f -name "*.orig" -print0 | xargs -0 rm -f
+ find $SCRYPT_DIR -type f -name "*~" -print0 | xargs -0 rm -f
+
+ declare -r variable_name=SCRYPT_PATCHES_`basename $patch .patch | sed s/-/_/`_SOURCES
+ # http://tldp.org/LDP/abs/html/ivr.html
+ eval declare -r sources=\$$variable_name
+ rm -f $patch
+ touch $patch
+ for i in $sources; do
+ LC_ALL=C TZ=UTC0 diff -aup $SCRYPT_DIR_ORIG/$i $SCRYPT_DIR/$i >> $patch && die "ERROR: No diff for patch $path in file $i"
+ done
+ echo "Generated patch $patch"
+ echo "NOTE To make sure there are not unwanted changes from conflicting patches, be sure to review the generated patch."
+}
+
+main $@
diff --git a/crypto/scrypt/lib/README b/crypto/scrypt/lib/README
new file mode 100644
index 000000000..3bb211e84
--- /dev/null
+++ b/crypto/scrypt/lib/README
@@ -0,0 +1,6 @@
+The source code under this directory is taken from the client for the
+Tarsnap online backup system (and released under the 2-clause BSD license
+with permission of the author); keeping this code in sync with the Tarsnap
+code is highly desirable and explains why there is some functionality
+included here which is not actually used by the scrypt file encryption
+utility.
diff --git a/crypto/scrypt/lib/crypto/crypto_scrypt-neon-salsa208.h b/crypto/scrypt/lib/crypto/crypto_scrypt-neon-salsa208.h
new file mode 100644
index 000000000..a3b1019a7
--- /dev/null
+++ b/crypto/scrypt/lib/crypto/crypto_scrypt-neon-salsa208.h
@@ -0,0 +1,120 @@
+/*
+ * version 20110505
+ * D. J. Bernstein
+ * Public domain.
+ *
+ * Based on crypto_core/salsa208/armneon/core.c from SUPERCOP 20130419
+ */
+
+#define ROUNDS 8
+static void
+salsa20_8_intrinsic(void * input)
+{
+ int i;
+
+ const uint32x4_t abab = {-1,0,-1,0};
+
+ /*
+ * This is modified since we only have one argument. Usually you'd rearrange
+ * the constant, key, and input bytes, but we just have one linear array to
+ * rearrange which is a bit easier.
+ */
+
+ /*
+ * Change the input to be diagonals as if it's a 4x4 matrix of 32-bit values.
+ */
+ uint32x4_t x0x5x10x15;
+ uint32x4_t x12x1x6x11;
+ uint32x4_t x8x13x2x7;
+ uint32x4_t x4x9x14x3;
+
+ uint32x4_t x0x1x10x11;
+ uint32x4_t x12x13x6x7;
+ uint32x4_t x8x9x2x3;
+ uint32x4_t x4x5x14x15;
+
+ uint32x4_t x0x1x2x3;
+ uint32x4_t x4x5x6x7;
+ uint32x4_t x8x9x10x11;
+ uint32x4_t x12x13x14x15;
+
+ x0x1x2x3 = vld1q_u8((uint8_t *) input);
+ x4x5x6x7 = vld1q_u8(16 + (uint8_t *) input);
+ x8x9x10x11 = vld1q_u8(32 + (uint8_t *) input);
+ x12x13x14x15 = vld1q_u8(48 + (uint8_t *) input);
+
+ x0x1x10x11 = vcombine_u32(vget_low_u32(x0x1x2x3), vget_high_u32(x8x9x10x11));
+ x4x5x14x15 = vcombine_u32(vget_low_u32(x4x5x6x7), vget_high_u32(x12x13x14x15));
+ x8x9x2x3 = vcombine_u32(vget_low_u32(x8x9x10x11), vget_high_u32(x0x1x2x3));
+ x12x13x6x7 = vcombine_u32(vget_low_u32(x12x13x14x15), vget_high_u32(x4x5x6x7));
+
+ x0x5x10x15 = vbslq_u32(abab,x0x1x10x11,x4x5x14x15);
+ x8x13x2x7 = vbslq_u32(abab,x8x9x2x3,x12x13x6x7);
+ x4x9x14x3 = vbslq_u32(abab,x4x5x14x15,x8x9x2x3);
+ x12x1x6x11 = vbslq_u32(abab,x12x13x6x7,x0x1x10x11);
+
+ uint32x4_t start0 = x0x5x10x15;
+ uint32x4_t start1 = x12x1x6x11;
+ uint32x4_t start3 = x4x9x14x3;
+ uint32x4_t start2 = x8x13x2x7;
+
+ /* From here on this should be the same as the SUPERCOP version. */
+
+ uint32x4_t diag0 = start0;
+ uint32x4_t diag1 = start1;
+ uint32x4_t diag2 = start2;
+ uint32x4_t diag3 = start3;
+
+ uint32x4_t a0;
+ uint32x4_t a1;
+ uint32x4_t a2;
+ uint32x4_t a3;
+
+ for (i = ROUNDS;i > 0;i -= 2) {
+ a0 = diag1 + diag0;
+ diag3 ^= vsriq_n_u32(vshlq_n_u32(a0,7),a0,25);
+ a1 = diag0 + diag3;
+ diag2 ^= vsriq_n_u32(vshlq_n_u32(a1,9),a1,23);
+ a2 = diag3 + diag2;
+ diag1 ^= vsriq_n_u32(vshlq_n_u32(a2,13),a2,19);
+ a3 = diag2 + diag1;
+ diag0 ^= vsriq_n_u32(vshlq_n_u32(a3,18),a3,14);
+
+ diag3 = vextq_u32(diag3,diag3,3);
+ diag2 = vextq_u32(diag2,diag2,2);
+ diag1 = vextq_u32(diag1,diag1,1);
+
+ a0 = diag3 + diag0;
+ diag1 ^= vsriq_n_u32(vshlq_n_u32(a0,7),a0,25);
+ a1 = diag0 + diag1;
+ diag2 ^= vsriq_n_u32(vshlq_n_u32(a1,9),a1,23);
+ a2 = diag1 + diag2;
+ diag3 ^= vsriq_n_u32(vshlq_n_u32(a2,13),a2,19);
+ a3 = diag2 + diag3;
+ diag0 ^= vsriq_n_u32(vshlq_n_u32(a3,18),a3,14);
+
+ diag1 = vextq_u32(diag1,diag1,3);
+ diag2 = vextq_u32(diag2,diag2,2);
+ diag3 = vextq_u32(diag3,diag3,1);
+ }
+
+ x0x5x10x15 = diag0 + start0;
+ x12x1x6x11 = diag1 + start1;
+ x8x13x2x7 = diag2 + start2;
+ x4x9x14x3 = diag3 + start3;
+
+ x0x1x10x11 = vbslq_u32(abab,x0x5x10x15,x12x1x6x11);
+ x12x13x6x7 = vbslq_u32(abab,x12x1x6x11,x8x13x2x7);
+ x8x9x2x3 = vbslq_u32(abab,x8x13x2x7,x4x9x14x3);
+ x4x5x14x15 = vbslq_u32(abab,x4x9x14x3,x0x5x10x15);
+
+ x0x1x2x3 = vcombine_u32(vget_low_u32(x0x1x10x11),vget_high_u32(x8x9x2x3));
+ x4x5x6x7 = vcombine_u32(vget_low_u32(x4x5x14x15),vget_high_u32(x12x13x6x7));
+ x8x9x10x11 = vcombine_u32(vget_low_u32(x8x9x2x3),vget_high_u32(x0x1x10x11));
+ x12x13x14x15 = vcombine_u32(vget_low_u32(x12x13x6x7),vget_high_u32(x4x5x14x15));
+
+ vst1q_u8((uint8_t *) input,(uint8x16_t) x0x1x2x3);
+ vst1q_u8(16 + (uint8_t *) input,(uint8x16_t) x4x5x6x7);
+ vst1q_u8(32 + (uint8_t *) input,(uint8x16_t) x8x9x10x11);
+ vst1q_u8(48 + (uint8_t *) input,(uint8x16_t) x12x13x14x15);
+}
diff --git a/crypto/scrypt/lib/crypto/crypto_scrypt-neon.c b/crypto/scrypt/lib/crypto/crypto_scrypt-neon.c
new file mode 100644
index 000000000..a3bf052b4
--- /dev/null
+++ b/crypto/scrypt/lib/crypto/crypto_scrypt-neon.c
@@ -0,0 +1,305 @@
+/*-
+ * Copyright 2009 Colin Percival
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This file was originally written by Colin Percival as part of the Tarsnap
+ * online backup system.
+ */
+#include "scrypt_platform.h"
+
+#include <machine/cpu-features.h>
+#include <arm_neon.h>
+
+#include <errno.h>
+#include <stdint.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef USE_OPENSSL_PBKDF2
+#include <openssl/evp.h>
+#else
+#include "sha256.h"
+#endif
+#include "sysendian.h"
+
+#include "crypto_scrypt.h"
+
+#include "crypto_scrypt-neon-salsa208.h"
+
+static void blkcpy(void *, void *, size_t);
+static void blkxor(void *, void *, size_t);
+void crypto_core_salsa208_armneon2(void *);
+static void blockmix_salsa8(uint8x16_t *, uint8x16_t *, uint8x16_t *, size_t);
+static uint64_t integerify(void *, size_t);
+static void smix(uint8_t *, size_t, uint64_t, void *, void *);
+
+static void
+blkcpy(void * dest, void * src, size_t len)
+{
+ uint8x16_t * D = dest;
+ uint8x16_t * S = src;
+ size_t L = len / 16;
+ size_t i;
+
+ for (i = 0; i < L; i++)
+ D[i] = S[i];
+}
+
+static void
+blkxor(void * dest, void * src, size_t len)
+{
+ uint8x16_t * D = dest;
+ uint8x16_t * S = src;
+ size_t L = len / 16;
+ size_t i;
+
+ for (i = 0; i < L; i++)
+ D[i] = veorq_u8(D[i], S[i]);
+}
+
+/**
+ * blockmix_salsa8(B, Y, r):
+ * Compute B = BlockMix_{salsa20/8, r}(B). The input B must be 128r bytes in
+ * length; the temporary space Y must also be the same size.
+ */
+static void
+blockmix_salsa8(uint8x16_t * Bin, uint8x16_t * Bout, uint8x16_t * X, size_t r)
+{
+ size_t i;
+
+ /* 1: X <-- B_{2r - 1} */
+ blkcpy(X, &Bin[8 * r - 4], 64);
+
+ /* 2: for i = 0 to 2r - 1 do */
+ for (i = 0; i < r; i++) {
+ /* 3: X <-- H(X \xor B_i) */
+ blkxor(X, &Bin[i * 8], 64);
+ salsa20_8_intrinsic((void *) X);
+
+ /* 4: Y_i <-- X */
+ /* 6: B' <-- (Y_0, Y_2 ... Y_{2r-2}, Y_1, Y_3 ... Y_{2r-1}) */
+ blkcpy(&Bout[i * 4], X, 64);
+
+ /* 3: X <-- H(X \xor B_i) */
+ blkxor(X, &Bin[i * 8 + 4], 64);
+ salsa20_8_intrinsic((void *) X);
+
+ /* 4: Y_i <-- X */
+ /* 6: B' <-- (Y_0, Y_2 ... Y_{2r-2}, Y_1, Y_3 ... Y_{2r-1}) */
+ blkcpy(&Bout[(r + i) * 4], X, 64);
+ }
+}
+
+/**
+ * integerify(B, r):
+ * Return the result of parsing B_{2r-1} as a little-endian integer.
+ */
+static uint64_t
+integerify(void * B, size_t r)
+{
+ uint8_t * X = (void*)((uintptr_t)(B) + (2 * r - 1) * 64);
+
+ return (le64dec(X));
+}
+
+/**
+ * smix(B, r, N, V, XY):
+ * Compute B = SMix_r(B, N). The input B must be 128r bytes in length; the
+ * temporary storage V must be 128rN bytes in length; the temporary storage
+ * XY must be 256r bytes in length. The value N must be a power of 2.
+ */
+static void
+smix(uint8_t * B, size_t r, uint64_t N, void * V, void * XY)
+{
+ uint8x16_t * X = XY;
+ uint8x16_t * Y = (void *)((uintptr_t)(XY) + 128 * r);
+ uint8x16_t * Z = (void *)((uintptr_t)(XY) + 256 * r);
+ uint32_t * X32 = (void *)X;
+ uint64_t i, j;
+ size_t k;
+
+ /* 1: X <-- B */
+ blkcpy(X, B, 128 * r);
+
+ /* 2: for i = 0 to N - 1 do */
+ for (i = 0; i < N; i += 2) {
+ /* 3: V_i <-- X */
+ blkcpy((void *)((uintptr_t)(V) + i * 128 * r), X, 128 * r);
+
+ /* 4: X <-- H(X) */
+ blockmix_salsa8(X, Y, Z, r);
+
+ /* 3: V_i <-- X */
+ blkcpy((void *)((uintptr_t)(V) + (i + 1) * 128 * r),
+ Y, 128 * r);
+
+ /* 4: X <-- H(X) */
+ blockmix_salsa8(Y, X, Z, r);
+ }
+
+ /* 6: for i = 0 to N - 1 do */
+ for (i = 0; i < N; i += 2) {
+ /* 7: j <-- Integerify(X) mod N */
+ j = integerify(X, r) & (N - 1);
+
+ /* 8: X <-- H(X \xor V_j) */
+ blkxor(X, (void *)((uintptr_t)(V) + j * 128 * r), 128 * r);
+ blockmix_salsa8(X, Y, Z, r);
+
+ /* 7: j <-- Integerify(X) mod N */
+ j = integerify(Y, r) & (N - 1);
+
+ /* 8: X <-- H(X \xor V_j) */
+ blkxor(Y, (void *)((uintptr_t)(V) + j * 128 * r), 128 * r);
+ blockmix_salsa8(Y, X, Z, r);
+ }
+
+ /* 10: B' <-- X */
+ blkcpy(B, X, 128 * r);
+}
+
+/**
+ * crypto_scrypt(passwd, passwdlen, salt, saltlen, N, r, p, buf, buflen):
+ * Compute scrypt(passwd[0 .. passwdlen - 1], salt[0 .. saltlen - 1], N, r,
+ * p, buflen) and write the result into buf. The parameters r, p, and buflen
+ * must satisfy r * p < 2^30 and buflen <= (2^32 - 1) * 32. The parameter N
+ * must be a power of 2.
+ *
+ * Return 0 on success; or -1 on error.
+ */
+int
+crypto_scrypt(const uint8_t * passwd, size_t passwdlen,
+ const uint8_t * salt, size_t saltlen, uint64_t N, uint32_t r, uint32_t p,
+ uint8_t * buf, size_t buflen)
+{
+ void * B0, * V0, * XY0;
+ uint8_t * B;
+ uint32_t * V;
+ uint32_t * XY;
+ uint32_t i;
+
+ /* Sanity-check parameters. */
+#if SIZE_MAX > UINT32_MAX
+ if (buflen > (((uint64_t)(1) << 32) - 1) * 32) {
+ errno = EFBIG;
+ goto err0;
+ }
+#endif
+ if ((uint64_t)(r) * (uint64_t)(p) >= (1 << 30)) {
+ errno = EFBIG;
+ goto err0;
+ }
+ if (((N & (N - 1)) != 0) || (N == 0)) {
+ errno = EINVAL;
+ goto err0;
+ }
+ if ((r > SIZE_MAX / 128 / p) ||
+#if SIZE_MAX / 256 <= UINT32_MAX
+ (r > SIZE_MAX / 256) ||
+#endif
+ (N > SIZE_MAX / 128 / r)) {
+ errno = ENOMEM;
+ goto err0;
+ }
+
+ /* Allocate memory. */
+#ifdef HAVE_POSIX_MEMALIGN
+ if ((errno = posix_memalign(&B0, 64, 128 * r * p)) != 0)
+ goto err0;
+ B = (uint8_t *)(B0);
+ if ((errno = posix_memalign(&XY0, 64, 256 * r + 64)) != 0)
+ goto err1;
+ XY = (uint32_t *)(XY0);
+#ifndef MAP_ANON
+ if ((errno = posix_memalign(&V0, 64, 128 * r * N)) != 0)
+ goto err2;
+ V = (uint32_t *)(V0);
+#endif
+#else
+ if ((B0 = malloc(128 * r * p + 63)) == NULL)
+ goto err0;
+ B = (uint8_t *)(((uintptr_t)(B0) + 63) & ~ (uintptr_t)(63));
+ if ((XY0 = malloc(256 * r + 64 + 63)) == NULL)
+ goto err1;
+ XY = (uint32_t *)(((uintptr_t)(XY0) + 63) & ~ (uintptr_t)(63));
+#ifndef MAP_ANON
+ if ((V0 = malloc(128 * r * N + 63)) == NULL)
+ goto err2;
+ V = (uint32_t *)(((uintptr_t)(V0) + 63) & ~ (uintptr_t)(63));
+#endif
+#endif
+#ifdef MAP_ANON
+ if ((V0 = mmap(NULL, 128 * r * N, PROT_READ | PROT_WRITE,
+#ifdef MAP_NOCORE
+ MAP_ANON | MAP_PRIVATE | MAP_NOCORE,
+#else
+ MAP_ANON | MAP_PRIVATE,
+#endif
+ -1, 0)) == MAP_FAILED)
+ goto err2;
+ V = (uint32_t *)(V0);
+#endif
+
+ /* 1: (B_0 ... B_{p-1}) <-- PBKDF2(P, S, 1, p * MFLen) */
+#ifdef USE_OPENSSL_PBKDF2
+ PKCS5_PBKDF2_HMAC((const char *)passwd, passwdlen, salt, saltlen, 1, EVP_sha256(), p * 128 * r, B);
+#else
+ PBKDF2_SHA256(passwd, passwdlen, salt, saltlen, 1, B, p * 128 * r);
+#endif
+
+ /* 2: for i = 0 to p - 1 do */
+ for (i = 0; i < p; i++) {
+ /* 3: B_i <-- MF(B_i, N) */
+ smix(&B[i * 128 * r], r, N, V, XY);
+ }
+
+ /* 5: DK <-- PBKDF2(P, B, 1, dkLen) */
+#ifdef USE_OPENSSL_PBKDF2
+ PKCS5_PBKDF2_HMAC((const char *)passwd, passwdlen, B, p * 128 * r, 1, EVP_sha256(), buflen, buf);
+#else
+ PBKDF2_SHA256(passwd, passwdlen, B, p * 128 * r, 1, buf, buflen);
+#endif
+
+ /* Free memory. */
+#ifdef MAP_ANON
+ if (munmap(V0, 128 * r * N))
+ goto err2;
+#else
+ free(V0);
+#endif
+ free(XY0);
+ free(B0);
+
+ /* Success! */
+ return (0);
+
+err2:
+ free(XY0);
+err1:
+ free(B0);
+err0:
+ /* Failure! */
+ return (-1);
+}
diff --git a/crypto/scrypt/lib/crypto/crypto_scrypt-ref.c b/crypto/scrypt/lib/crypto/crypto_scrypt-ref.c
new file mode 100644
index 000000000..abe23eaa5
--- /dev/null
+++ b/crypto/scrypt/lib/crypto/crypto_scrypt-ref.c
@@ -0,0 +1,296 @@
+/*-
+ * Copyright 2009 Colin Percival
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This file was originally written by Colin Percival as part of the Tarsnap
+ * online backup system.
+ */
+#include "scrypt_platform.h"
+
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef USE_OPENSSL_PBKDF2
+#include <openssl/evp.h>
+#else
+#include "sha256.h"
+#endif
+#include "sysendian.h"
+
+#include "crypto_scrypt.h"
+
+static void blkcpy(uint8_t *, uint8_t *, size_t);
+static void blkxor(uint8_t *, uint8_t *, size_t);
+static void salsa20_8(uint8_t[64]);
+static void blockmix_salsa8(uint8_t *, uint8_t *, size_t);
+static uint64_t integerify(uint8_t *, size_t);
+static void smix(uint8_t *, size_t, uint64_t, uint8_t *, uint8_t *);
+
+static void
+blkcpy(uint8_t * dest, uint8_t * src, size_t len)
+{
+ size_t i;
+
+ for (i = 0; i < len; i++)
+ dest[i] = src[i];
+}
+
+static void
+blkxor(uint8_t * dest, uint8_t * src, size_t len)
+{
+ size_t i;
+
+ for (i = 0; i < len; i++)
+ dest[i] ^= src[i];
+}
+
+/**
+ * salsa20_8(B):
+ * Apply the salsa20/8 core to the provided block.
+ */
+static void
+salsa20_8(uint8_t B[64])
+{
+ uint32_t B32[16];
+ uint32_t x[16];
+ size_t i;
+
+ /* Convert little-endian values in. */
+ for (i = 0; i < 16; i++)
+ B32[i] = le32dec(&B[i * 4]);
+
+ /* Compute x = doubleround^4(B32). */
+ for (i = 0; i < 16; i++)
+ x[i] = B32[i];
+ for (i = 0; i < 8; i += 2) {
+#define R(a,b) (((a) << (b)) | ((a) >> (32 - (b))))
+ /* Operate on columns. */
+ x[ 4] ^= R(x[ 0]+x[12], 7); x[ 8] ^= R(x[ 4]+x[ 0], 9);
+ x[12] ^= R(x[ 8]+x[ 4],13); x[ 0] ^= R(x[12]+x[ 8],18);
+
+ x[ 9] ^= R(x[ 5]+x[ 1], 7); x[13] ^= R(x[ 9]+x[ 5], 9);
+ x[ 1] ^= R(x[13]+x[ 9],13); x[ 5] ^= R(x[ 1]+x[13],18);
+
+ x[14] ^= R(x[10]+x[ 6], 7); x[ 2] ^= R(x[14]+x[10], 9);
+ x[ 6] ^= R(x[ 2]+x[14],13); x[10] ^= R(x[ 6]+x[ 2],18);
+
+ x[ 3] ^= R(x[15]+x[11], 7); x[ 7] ^= R(x[ 3]+x[15], 9);
+ x[11] ^= R(x[ 7]+x[ 3],13); x[15] ^= R(x[11]+x[ 7],18);
+
+ /* Operate on rows. */
+ x[ 1] ^= R(x[ 0]+x[ 3], 7); x[ 2] ^= R(x[ 1]+x[ 0], 9);
+ x[ 3] ^= R(x[ 2]+x[ 1],13); x[ 0] ^= R(x[ 3]+x[ 2],18);
+
+ x[ 6] ^= R(x[ 5]+x[ 4], 7); x[ 7] ^= R(x[ 6]+x[ 5], 9);
+ x[ 4] ^= R(x[ 7]+x[ 6],13); x[ 5] ^= R(x[ 4]+x[ 7],18);
+
+ x[11] ^= R(x[10]+x[ 9], 7); x[ 8] ^= R(x[11]+x[10], 9);
+ x[ 9] ^= R(x[ 8]+x[11],13); x[10] ^= R(x[ 9]+x[ 8],18);
+
+ x[12] ^= R(x[15]+x[14], 7); x[13] ^= R(x[12]+x[15], 9);
+ x[14] ^= R(x[13]+x[12],13); x[15] ^= R(x[14]+x[13],18);
+#undef R
+ }
+
+ /* Compute B32 = B32 + x. */
+ for (i = 0; i < 16; i++)
+ B32[i] += x[i];
+
+ /* Convert little-endian values out. */
+ for (i = 0; i < 16; i++)
+ le32enc(&B[4 * i], B32[i]);
+}
+
+/**
+ * blockmix_salsa8(B, Y, r):
+ * Compute B = BlockMix_{salsa20/8, r}(B). The input B must be 128r bytes in
+ * length; the temporary space Y must also be the same size.
+ */
+static void
+blockmix_salsa8(uint8_t * B, uint8_t * Y, size_t r)
+{
+ uint8_t X[64];
+ size_t i;
+
+ /* 1: X <-- B_{2r - 1} */
+ blkcpy(X, &B[(2 * r - 1) * 64], 64);
+
+ /* 2: for i = 0 to 2r - 1 do */
+ for (i = 0; i < 2 * r; i++) {
+ /* 3: X <-- H(X \xor B_i) */
+ blkxor(X, &B[i * 64], 64);
+ salsa20_8(X);
+
+ /* 4: Y_i <-- X */
+ blkcpy(&Y[i * 64], X, 64);
+ }
+
+ /* 6: B' <-- (Y_0, Y_2 ... Y_{2r-2}, Y_1, Y_3 ... Y_{2r-1}) */
+ for (i = 0; i < r; i++)
+ blkcpy(&B[i * 64], &Y[(i * 2) * 64], 64);
+ for (i = 0; i < r; i++)
+ blkcpy(&B[(i + r) * 64], &Y[(i * 2 + 1) * 64], 64);
+}
+
+/**
+ * integerify(B, r):
+ * Return the result of parsing B_{2r-1} as a little-endian integer.
+ */
+static uint64_t
+integerify(uint8_t * B, size_t r)
+{
+ uint8_t * X = &B[(2 * r - 1) * 64];
+
+ return (le64dec(X));
+}
+
+/**
+ * smix(B, r, N, V, XY):
+ * Compute B = SMix_r(B, N). The input B must be 128r bytes in length; the
+ * temporary storage V must be 128rN bytes in length; the temporary storage
+ * XY must be 256r bytes in length. The value N must be a power of 2.
+ */
+static void
+smix(uint8_t * B, size_t r, uint64_t N, uint8_t * V, uint8_t * XY)
+{
+ uint8_t * X = XY;
+ uint8_t * Y = &XY[128 * r];
+ uint64_t i;
+ uint64_t j;
+
+ /* 1: X <-- B */
+ blkcpy(X, B, 128 * r);
+
+ /* 2: for i = 0 to N - 1 do */
+ for (i = 0; i < N; i++) {
+ /* 3: V_i <-- X */
+ blkcpy(&V[i * (128 * r)], X, 128 * r);
+
+ /* 4: X <-- H(X) */
+ blockmix_salsa8(X, Y, r);
+ }
+
+ /* 6: for i = 0 to N - 1 do */
+ for (i = 0; i < N; i++) {
+ /* 7: j <-- Integerify(X) mod N */
+ j = integerify(X, r) & (N - 1);
+
+ /* 8: X <-- H(X \xor V_j) */
+ blkxor(X, &V[j * (128 * r)], 128 * r);
+ blockmix_salsa8(X, Y, r);
+ }
+
+ /* 10: B' <-- X */
+ blkcpy(B, X, 128 * r);
+}
+
+/**
+ * crypto_scrypt(passwd, passwdlen, salt, saltlen, N, r, p, buf, buflen):
+ * Compute scrypt(passwd[0 .. passwdlen - 1], salt[0 .. saltlen - 1], N, r,
+ * p, buflen) and write the result into buf. The parameters r, p, and buflen
+ * must satisfy r * p < 2^30 and buflen <= (2^32 - 1) * 32. The parameter N
+ * must be a power of 2.
+ *
+ * Return 0 on success; or -1 on error.
+ */
+int
+crypto_scrypt(const uint8_t * passwd, size_t passwdlen,
+ const uint8_t * salt, size_t saltlen, uint64_t N, uint32_t r, uint32_t p,
+ uint8_t * buf, size_t buflen)
+{
+ uint8_t * B;
+ uint8_t * V;
+ uint8_t * XY;
+ uint32_t i;
+
+ /* Sanity-check parameters. */
+#if SIZE_MAX > UINT32_MAX
+ if (buflen > (((uint64_t)(1) << 32) - 1) * 32) {
+ errno = EFBIG;
+ goto err0;
+ }
+#endif
+ if ((uint64_t)(r) * (uint64_t)(p) >= (1 << 30)) {
+ errno = EFBIG;
+ goto err0;
+ }
+ if (((N & (N - 1)) != 0) || (N == 0)) {
+ errno = EINVAL;
+ goto err0;
+ }
+ if ((r > SIZE_MAX / 128 / p) ||
+#if SIZE_MAX / 256 <= UINT32_MAX
+ (r > SIZE_MAX / 256) ||
+#endif
+ (N > SIZE_MAX / 128 / r)) {
+ errno = ENOMEM;
+ goto err0;
+ }
+
+ /* Allocate memory. */
+ if ((B = malloc(128 * r * p)) == NULL)
+ goto err0;
+ if ((XY = malloc(256 * r)) == NULL)
+ goto err1;
+ if ((V = malloc(128 * r * N)) == NULL)
+ goto err2;
+
+ /* 1: (B_0 ... B_{p-1}) <-- PBKDF2(P, S, 1, p * MFLen) */
+#ifdef USE_OPENSSL_PBKDF2
+ PKCS5_PBKDF2_HMAC((const char *)passwd, passwdlen, salt, saltlen, 1, EVP_sha256(), p * 128 * r, B);
+#else
+ PBKDF2_SHA256(passwd, passwdlen, salt, saltlen, 1, B, p * 128 * r);
+#endif
+
+ /* 2: for i = 0 to p - 1 do */
+ for (i = 0; i < p; i++) {
+ /* 3: B_i <-- MF(B_i, N) */
+ smix(&B[i * 128 * r], r, N, V, XY);
+ }
+
+ /* 5: DK <-- PBKDF2(P, B, 1, dkLen) */
+#ifdef USE_OPENSSL_PBKDF2
+ PKCS5_PBKDF2_HMAC((const char *)passwd, passwdlen, B, p * 128 * r, 1, EVP_sha256(), buflen, buf);
+#else
+ PBKDF2_SHA256(passwd, passwdlen, B, p * 128 * r, 1, buf, buflen);
+#endif
+
+ /* Free memory. */
+ free(V);
+ free(XY);
+ free(B);
+
+ /* Success! */
+ return (0);
+
+err2:
+ free(XY);
+err1:
+ free(B);
+err0:
+ /* Failure! */
+ return (-1);
+}
diff --git a/crypto/scrypt/lib/crypto/crypto_scrypt-sse.c b/crypto/scrypt/lib/crypto/crypto_scrypt-sse.c
new file mode 100644
index 000000000..dd18f291c
--- /dev/null
+++ b/crypto/scrypt/lib/crypto/crypto_scrypt-sse.c
@@ -0,0 +1,378 @@
+/*-
+ * Copyright 2009 Colin Percival
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This file was originally written by Colin Percival as part of the Tarsnap
+ * online backup system.
+ */
+#include "scrypt_platform.h"
+
+#include <sys/types.h>
+#include <sys/mman.h>
+
+#include <emmintrin.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef USE_OPENSSL_PBKDF2
+#include <openssl/evp.h>
+#else
+#include "sha256.h"
+#endif
+#include "sysendian.h"
+
+#include "crypto_scrypt.h"
+
+static void blkcpy(void *, void *, size_t);
+static void blkxor(void *, void *, size_t);
+static void salsa20_8(__m128i *);
+static void blockmix_salsa8(__m128i *, __m128i *, __m128i *, size_t);
+static uint64_t integerify(void *, size_t);
+static void smix(uint8_t *, size_t, uint64_t, void *, void *);
+
+static void
+blkcpy(void * dest, void * src, size_t len)
+{
+ __m128i * D = dest;
+ __m128i * S = src;
+ size_t L = len / 16;
+ size_t i;
+
+ for (i = 0; i < L; i++)
+ D[i] = S[i];
+}
+
+static void
+blkxor(void * dest, void * src, size_t len)
+{
+ __m128i * D = dest;
+ __m128i * S = src;
+ size_t L = len / 16;
+ size_t i;
+
+ for (i = 0; i < L; i++)
+ D[i] = _mm_xor_si128(D[i], S[i]);
+}
+
+/**
+ * salsa20_8(B):
+ * Apply the salsa20/8 core to the provided block.
+ */
+static void
+salsa20_8(__m128i B[4])
+{
+ __m128i X0, X1, X2, X3;
+ __m128i T;
+ size_t i;
+
+ X0 = B[0];
+ X1 = B[1];
+ X2 = B[2];
+ X3 = B[3];
+
+ for (i = 0; i < 8; i += 2) {
+ /* Operate on "columns". */
+ T = _mm_add_epi32(X0, X3);
+ X1 = _mm_xor_si128(X1, _mm_slli_epi32(T, 7));
+ X1 = _mm_xor_si128(X1, _mm_srli_epi32(T, 25));
+ T = _mm_add_epi32(X1, X0);
+ X2 = _mm_xor_si128(X2, _mm_slli_epi32(T, 9));
+ X2 = _mm_xor_si128(X2, _mm_srli_epi32(T, 23));
+ T = _mm_add_epi32(X2, X1);
+ X3 = _mm_xor_si128(X3, _mm_slli_epi32(T, 13));
+ X3 = _mm_xor_si128(X3, _mm_srli_epi32(T, 19));
+ T = _mm_add_epi32(X3, X2);
+ X0 = _mm_xor_si128(X0, _mm_slli_epi32(T, 18));
+ X0 = _mm_xor_si128(X0, _mm_srli_epi32(T, 14));
+
+ /* Rearrange data. */
+ X1 = _mm_shuffle_epi32(X1, 0x93);
+ X2 = _mm_shuffle_epi32(X2, 0x4E);
+ X3 = _mm_shuffle_epi32(X3, 0x39);
+
+ /* Operate on "rows". */
+ T = _mm_add_epi32(X0, X1);
+ X3 = _mm_xor_si128(X3, _mm_slli_epi32(T, 7));
+ X3 = _mm_xor_si128(X3, _mm_srli_epi32(T, 25));
+ T = _mm_add_epi32(X3, X0);
+ X2 = _mm_xor_si128(X2, _mm_slli_epi32(T, 9));
+ X2 = _mm_xor_si128(X2, _mm_srli_epi32(T, 23));
+ T = _mm_add_epi32(X2, X3);
+ X1 = _mm_xor_si128(X1, _mm_slli_epi32(T, 13));
+ X1 = _mm_xor_si128(X1, _mm_srli_epi32(T, 19));
+ T = _mm_add_epi32(X1, X2);
+ X0 = _mm_xor_si128(X0, _mm_slli_epi32(T, 18));
+ X0 = _mm_xor_si128(X0, _mm_srli_epi32(T, 14));
+
+ /* Rearrange data. */
+ X1 = _mm_shuffle_epi32(X1, 0x39);
+ X2 = _mm_shuffle_epi32(X2, 0x4E);
+ X3 = _mm_shuffle_epi32(X3, 0x93);
+ }
+
+ B[0] = _mm_add_epi32(B[0], X0);
+ B[1] = _mm_add_epi32(B[1], X1);
+ B[2] = _mm_add_epi32(B[2], X2);
+ B[3] = _mm_add_epi32(B[3], X3);
+}
+
+/**
+ * blockmix_salsa8(Bin, Bout, X, r):
+ * Compute Bout = BlockMix_{salsa20/8, r}(Bin). The input Bin must be 128r
+ * bytes in length; the output Bout must also be the same size. The
+ * temporary space X must be 64 bytes.
+ */
+static void
+blockmix_salsa8(__m128i * Bin, __m128i * Bout, __m128i * X, size_t r)
+{
+ size_t i;
+
+ /* 1: X <-- B_{2r - 1} */
+ blkcpy(X, &Bin[8 * r - 4], 64);
+
+ /* 2: for i = 0 to 2r - 1 do */
+ for (i = 0; i < r; i++) {
+ /* 3: X <-- H(X \xor B_i) */
+ blkxor(X, &Bin[i * 8], 64);
+ salsa20_8(X);
+
+ /* 4: Y_i <-- X */
+ /* 6: B' <-- (Y_0, Y_2 ... Y_{2r-2}, Y_1, Y_3 ... Y_{2r-1}) */
+ blkcpy(&Bout[i * 4], X, 64);
+
+ /* 3: X <-- H(X \xor B_i) */
+ blkxor(X, &Bin[i * 8 + 4], 64);
+ salsa20_8(X);
+
+ /* 4: Y_i <-- X */
+ /* 6: B' <-- (Y_0, Y_2 ... Y_{2r-2}, Y_1, Y_3 ... Y_{2r-1}) */
+ blkcpy(&Bout[(r + i) * 4], X, 64);
+ }
+}
+
+/**
+ * integerify(B, r):
+ * Return the result of parsing B_{2r-1} as a little-endian integer.
+ */
+static uint64_t
+integerify(void * B, size_t r)
+{
+ uint32_t * X = (void *)((uintptr_t)(B) + (2 * r - 1) * 64);
+
+ return (((uint64_t)(X[13]) << 32) + X[0]);
+}
+
+/**
+ * smix(B, r, N, V, XY):
+ * Compute B = SMix_r(B, N). The input B must be 128r bytes in length;
+ * the temporary storage V must be 128rN bytes in length; the temporary
+ * storage XY must be 256r + 64 bytes in length. The value N must be a
+ * power of 2 greater than 1. The arrays B, V, and XY must be aligned to a
+ * multiple of 64 bytes.
+ */
+static void
+smix(uint8_t * B, size_t r, uint64_t N, void * V, void * XY)
+{
+ __m128i * X = XY;
+ __m128i * Y = (void *)((uintptr_t)(XY) + 128 * r);
+ __m128i * Z = (void *)((uintptr_t)(XY) + 256 * r);
+ uint32_t * X32 = (void *)X;
+ uint64_t i, j;
+ size_t k;
+
+ /* 1: X <-- B */
+ for (k = 0; k < 2 * r; k++) {
+ for (i = 0; i < 16; i++) {
+ X32[k * 16 + i] =
+ le32dec(&B[(k * 16 + (i * 5 % 16)) * 4]);
+ }
+ }
+
+ /* 2: for i = 0 to N - 1 do */
+ for (i = 0; i < N; i += 2) {
+ /* 3: V_i <-- X */
+ blkcpy((void *)((uintptr_t)(V) + i * 128 * r), X, 128 * r);
+
+ /* 4: X <-- H(X) */
+ blockmix_salsa8(X, Y, Z, r);
+
+ /* 3: V_i <-- X */
+ blkcpy((void *)((uintptr_t)(V) + (i + 1) * 128 * r),
+ Y, 128 * r);
+
+ /* 4: X <-- H(X) */
+ blockmix_salsa8(Y, X, Z, r);
+ }
+
+ /* 6: for i = 0 to N - 1 do */
+ for (i = 0; i < N; i += 2) {
+ /* 7: j <-- Integerify(X) mod N */
+ j = integerify(X, r) & (N - 1);
+
+ /* 8: X <-- H(X \xor V_j) */
+ blkxor(X, (void *)((uintptr_t)(V) + j * 128 * r), 128 * r);
+ blockmix_salsa8(X, Y, Z, r);
+
+ /* 7: j <-- Integerify(X) mod N */
+ j = integerify(Y, r) & (N - 1);
+
+ /* 8: X <-- H(X \xor V_j) */
+ blkxor(Y, (void *)((uintptr_t)(V) + j * 128 * r), 128 * r);
+ blockmix_salsa8(Y, X, Z, r);
+ }
+
+ /* 10: B' <-- X */
+ for (k = 0; k < 2 * r; k++) {
+ for (i = 0; i < 16; i++) {
+ le32enc(&B[(k * 16 + (i * 5 % 16)) * 4],
+ X32[k * 16 + i]);
+ }
+ }
+}
+
+/**
+ * crypto_scrypt(passwd, passwdlen, salt, saltlen, N, r, p, buf, buflen):
+ * Compute scrypt(passwd[0 .. passwdlen - 1], salt[0 .. saltlen - 1], N, r,
+ * p, buflen) and write the result into buf. The parameters r, p, and buflen
+ * must satisfy r * p < 2^30 and buflen <= (2^32 - 1) * 32. The parameter N
+ * must be a power of 2 greater than 1.
+ *
+ * Return 0 on success; or -1 on error.
+ */
+int
+crypto_scrypt(const uint8_t * passwd, size_t passwdlen,
+ const uint8_t * salt, size_t saltlen, uint64_t N, uint32_t r, uint32_t p,
+ uint8_t * buf, size_t buflen)
+{
+ void * B0, * V0, * XY0;
+ uint8_t * B;
+ uint32_t * V;
+ uint32_t * XY;
+ uint32_t i;
+
+ /* Sanity-check parameters. */
+#if SIZE_MAX > UINT32_MAX
+ if (buflen > (((uint64_t)(1) << 32) - 1) * 32) {
+ errno = EFBIG;
+ goto err0;
+ }
+#endif
+ if ((uint64_t)(r) * (uint64_t)(p) >= (1 << 30)) {
+ errno = EFBIG;
+ goto err0;
+ }
+ if (((N & (N - 1)) != 0) || (N == 0)) {
+ errno = EINVAL;
+ goto err0;
+ }
+ if ((r > SIZE_MAX / 128 / p) ||
+#if SIZE_MAX / 256 <= UINT32_MAX
+ (r > (SIZE_MAX - 64) / 256) ||
+#endif
+ (N > SIZE_MAX / 128 / r)) {
+ errno = ENOMEM;
+ goto err0;
+ }
+
+ /* Allocate memory. */
+#ifdef HAVE_POSIX_MEMALIGN
+ if ((errno = posix_memalign(&B0, 64, 128 * r * p)) != 0)
+ goto err0;
+ B = (uint8_t *)(B0);
+ if ((errno = posix_memalign(&XY0, 64, 256 * r + 64)) != 0)
+ goto err1;
+ XY = (uint32_t *)(XY0);
+#ifndef MAP_ANON
+ if ((errno = posix_memalign(&V0, 64, 128 * r * N)) != 0)
+ goto err2;
+ V = (uint32_t *)(V0);
+#endif
+#else
+ if ((B0 = malloc(128 * r * p + 63)) == NULL)
+ goto err0;
+ B = (uint8_t *)(((uintptr_t)(B0) + 63) & ~ (uintptr_t)(63));
+ if ((XY0 = malloc(256 * r + 64 + 63)) == NULL)
+ goto err1;
+ XY = (uint32_t *)(((uintptr_t)(XY0) + 63) & ~ (uintptr_t)(63));
+#ifndef MAP_ANON
+ if ((V0 = malloc(128 * r * N + 63)) == NULL)
+ goto err2;
+ V = (uint32_t *)(((uintptr_t)(V0) + 63) & ~ (uintptr_t)(63));
+#endif
+#endif
+#ifdef MAP_ANON
+ if ((V0 = mmap(NULL, 128 * r * N, PROT_READ | PROT_WRITE,
+#ifdef MAP_NOCORE
+ MAP_ANON | MAP_PRIVATE | MAP_NOCORE,
+#else
+ MAP_ANON | MAP_PRIVATE,
+#endif
+ -1, 0)) == MAP_FAILED)
+ goto err2;
+ V = (uint32_t *)(V0);
+#endif
+
+ /* 1: (B_0 ... B_{p-1}) <-- PBKDF2(P, S, 1, p * MFLen) */
+#ifdef USE_OPENSSL_PBKDF2
+ PKCS5_PBKDF2_HMAC((const char *)passwd, passwdlen, salt, saltlen, 1, EVP_sha256(), p * 128 * r, B);
+#else
+ PBKDF2_SHA256(passwd, passwdlen, salt, saltlen, 1, B, p * 128 * r);
+#endif
+
+ /* 2: for i = 0 to p - 1 do */
+ for (i = 0; i < p; i++) {
+ /* 3: B_i <-- MF(B_i, N) */
+ smix(&B[i * 128 * r], r, N, V, XY);
+ }
+
+ /* 5: DK <-- PBKDF2(P, B, 1, dkLen) */
+#ifdef USE_OPENSSL_PBKDF2
+ PKCS5_PBKDF2_HMAC((const char *)passwd, passwdlen, B, p * 128 * r, 1, EVP_sha256(), buflen, buf);
+#else
+ PBKDF2_SHA256(passwd, passwdlen, B, p * 128 * r, 1, buf, buflen);
+#endif
+
+ /* Free memory. */
+#ifdef MAP_ANON
+ if (munmap(V0, 128 * r * N))
+ goto err2;
+#else
+ free(V0);
+#endif
+ free(XY0);
+ free(B0);
+
+ /* Success! */
+ return (0);
+
+err2:
+ free(XY0);
+err1:
+ free(B0);
+err0:
+ /* Failure! */
+ return (-1);
+}
diff --git a/crypto/scrypt/lib/crypto/crypto_scrypt.h b/crypto/scrypt/lib/crypto/crypto_scrypt.h
new file mode 100644
index 000000000..f72e1f4b0
--- /dev/null
+++ b/crypto/scrypt/lib/crypto/crypto_scrypt.h
@@ -0,0 +1,46 @@
+/*-
+ * Copyright 2009 Colin Percival
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This file was originally written by Colin Percival as part of the Tarsnap
+ * online backup system.
+ */
+#ifndef _CRYPTO_SCRYPT_H_
+#define _CRYPTO_SCRYPT_H_
+
+#include <stdint.h>
+
+/**
+ * crypto_scrypt(passwd, passwdlen, salt, saltlen, N, r, p, buf, buflen):
+ * Compute scrypt(passwd[0 .. passwdlen - 1], salt[0 .. saltlen - 1], N, r,
+ * p, buflen) and write the result into buf. The parameters r, p, and buflen
+ * must satisfy r * p < 2^30 and buflen <= (2^32 - 1) * 32. The parameter N
+ * must be a power of 2 greater than 1.
+ *
+ * Return 0 on success; or -1 on error.
+ */
+int crypto_scrypt(const uint8_t *, size_t, const uint8_t *, size_t, uint64_t,
+ uint32_t, uint32_t, uint8_t *, size_t);
+
+#endif /* !_CRYPTO_SCRYPT_H_ */
diff --git a/crypto/scrypt/lib/util/sysendian.h b/crypto/scrypt/lib/util/sysendian.h
new file mode 100644
index 000000000..62ef31a42
--- /dev/null
+++ b/crypto/scrypt/lib/util/sysendian.h
@@ -0,0 +1,140 @@
+/*-
+ * Copyright 2007-2009 Colin Percival
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This file was originally written by Colin Percival as part of the Tarsnap
+ * online backup system.
+ */
+#ifndef _SYSENDIAN_H_
+#define _SYSENDIAN_H_
+
+#include "scrypt_platform.h"
+
+/* If we don't have be64enc, the <sys/endian.h> we have isn't usable. */
+#if !HAVE_DECL_BE64ENC
+#undef HAVE_SYS_ENDIAN_H
+#endif
+
+#ifdef HAVE_SYS_ENDIAN_H
+
+#include <sys/endian.h>
+
+#else
+
+#include <stdint.h>
+
+static inline uint32_t
+be32dec(const void *pp)
+{
+ const uint8_t *p = (uint8_t const *)pp;
+
+ return ((uint32_t)(p[3]) + ((uint32_t)(p[2]) << 8) +
+ ((uint32_t)(p[1]) << 16) + ((uint32_t)(p[0]) << 24));
+}
+
+static inline void
+be32enc(void *pp, uint32_t x)
+{
+ uint8_t * p = (uint8_t *)pp;
+
+ p[3] = x & 0xff;
+ p[2] = (x >> 8) & 0xff;
+ p[1] = (x >> 16) & 0xff;
+ p[0] = (x >> 24) & 0xff;
+}
+
+static inline uint64_t
+be64dec(const void *pp)
+{
+ const uint8_t *p = (uint8_t const *)pp;
+
+ return ((uint64_t)(p[7]) + ((uint64_t)(p[6]) << 8) +
+ ((uint64_t)(p[5]) << 16) + ((uint64_t)(p[4]) << 24) +
+ ((uint64_t)(p[3]) << 32) + ((uint64_t)(p[2]) << 40) +
+ ((uint64_t)(p[1]) << 48) + ((uint64_t)(p[0]) << 56));
+}
+
+static inline void
+be64enc(void *pp, uint64_t x)
+{
+ uint8_t * p = (uint8_t *)pp;
+
+ p[7] = x & 0xff;
+ p[6] = (x >> 8) & 0xff;
+ p[5] = (x >> 16) & 0xff;
+ p[4] = (x >> 24) & 0xff;
+ p[3] = (x >> 32) & 0xff;
+ p[2] = (x >> 40) & 0xff;
+ p[1] = (x >> 48) & 0xff;
+ p[0] = (x >> 56) & 0xff;
+}
+
+static inline uint32_t
+le32dec(const void *pp)
+{
+ const uint8_t *p = (uint8_t const *)pp;
+
+ return ((uint32_t)(p[0]) + ((uint32_t)(p[1]) << 8) +
+ ((uint32_t)(p[2]) << 16) + ((uint32_t)(p[3]) << 24));
+}
+
+static inline void
+le32enc(void *pp, uint32_t x)
+{
+ uint8_t * p = (uint8_t *)pp;
+
+ p[0] = x & 0xff;
+ p[1] = (x >> 8) & 0xff;
+ p[2] = (x >> 16) & 0xff;
+ p[3] = (x >> 24) & 0xff;
+}
+
+static inline uint64_t
+le64dec(const void *pp)
+{
+ const uint8_t *p = (uint8_t const *)pp;
+
+ return ((uint64_t)(p[0]) + ((uint64_t)(p[1]) << 8) +
+ ((uint64_t)(p[2]) << 16) + ((uint64_t)(p[3]) << 24) +
+ ((uint64_t)(p[4]) << 32) + ((uint64_t)(p[5]) << 40) +
+ ((uint64_t)(p[6]) << 48) + ((uint64_t)(p[7]) << 56));
+}
+
+static inline void
+le64enc(void *pp, uint64_t x)
+{
+ uint8_t * p = (uint8_t *)pp;
+
+ p[0] = x & 0xff;
+ p[1] = (x >> 8) & 0xff;
+ p[2] = (x >> 16) & 0xff;
+ p[3] = (x >> 24) & 0xff;
+ p[4] = (x >> 32) & 0xff;
+ p[5] = (x >> 40) & 0xff;
+ p[6] = (x >> 48) & 0xff;
+ p[7] = (x >> 56) & 0xff;
+}
+#endif /* !HAVE_SYS_ENDIAN_H */
+
+#endif /* !_SYSENDIAN_H_ */
diff --git a/crypto/scrypt/patches/README b/crypto/scrypt/patches/README
new file mode 100644
index 000000000..353ddbbc7
--- /dev/null
+++ b/crypto/scrypt/patches/README
@@ -0,0 +1,11 @@
+bionic.patch:
+
+Allows scrypt to compile against bionic.
+
+use_openssl_pbkdf2.patch:
+
+Uses the PBKDF2 function from OpenSSL (it uses accelerated SHA256)
+
+arm-neon.patch:
+
+Adds NEON acceleration for the Salsa20/8 mixing function.
diff --git a/crypto/scrypt/patches/arm-neon.patch b/crypto/scrypt/patches/arm-neon.patch
new file mode 100644
index 000000000..7197f9968
--- /dev/null
+++ b/crypto/scrypt/patches/arm-neon.patch
@@ -0,0 +1,437 @@
+diff --git a/lib/crypto/crypto_scrypt-neon-salsa208.h b/lib/crypto/crypto_scrypt-neon-salsa208.h
+new file mode 100644
+index 0000000..a3b1019
+--- /dev/null
++++ b/lib/crypto/crypto_scrypt-neon-salsa208.h
+@@ -0,0 +1,120 @@
++/*
++ * version 20110505
++ * D. J. Bernstein
++ * Public domain.
++ *
++ * Based on crypto_core/salsa208/armneon/core.c from SUPERCOP 20130419
++ */
++
++#define ROUNDS 8
++static void
++salsa20_8_intrinsic(void * input)
++{
++ int i;
++
++ const uint32x4_t abab = {-1,0,-1,0};
++
++ /*
++ * This is modified since we only have one argument. Usually you'd rearrange
++ * the constant, key, and input bytes, but we just have one linear array to
++ * rearrange which is a bit easier.
++ */
++
++ /*
++ * Change the input to be diagonals as if it's a 4x4 matrix of 32-bit values.
++ */
++ uint32x4_t x0x5x10x15;
++ uint32x4_t x12x1x6x11;
++ uint32x4_t x8x13x2x7;
++ uint32x4_t x4x9x14x3;
++
++ uint32x4_t x0x1x10x11;
++ uint32x4_t x12x13x6x7;
++ uint32x4_t x8x9x2x3;
++ uint32x4_t x4x5x14x15;
++
++ uint32x4_t x0x1x2x3;
++ uint32x4_t x4x5x6x7;
++ uint32x4_t x8x9x10x11;
++ uint32x4_t x12x13x14x15;
++
++ x0x1x2x3 = vld1q_u8((uint8_t *) input);
++ x4x5x6x7 = vld1q_u8(16 + (uint8_t *) input);
++ x8x9x10x11 = vld1q_u8(32 + (uint8_t *) input);
++ x12x13x14x15 = vld1q_u8(48 + (uint8_t *) input);
++
++ x0x1x10x11 = vcombine_u32(vget_low_u32(x0x1x2x3), vget_high_u32(x8x9x10x11));
++ x4x5x14x15 = vcombine_u32(vget_low_u32(x4x5x6x7), vget_high_u32(x12x13x14x15));
++ x8x9x2x3 = vcombine_u32(vget_low_u32(x8x9x10x11), vget_high_u32(x0x1x2x3));
++ x12x13x6x7 = vcombine_u32(vget_low_u32(x12x13x14x15), vget_high_u32(x4x5x6x7));
++
++ x0x5x10x15 = vbslq_u32(abab,x0x1x10x11,x4x5x14x15);
++ x8x13x2x7 = vbslq_u32(abab,x8x9x2x3,x12x13x6x7);
++ x4x9x14x3 = vbslq_u32(abab,x4x5x14x15,x8x9x2x3);
++ x12x1x6x11 = vbslq_u32(abab,x12x13x6x7,x0x1x10x11);
++
++ uint32x4_t start0 = x0x5x10x15;
++ uint32x4_t start1 = x12x1x6x11;
++ uint32x4_t start3 = x4x9x14x3;
++ uint32x4_t start2 = x8x13x2x7;
++
++ /* From here on this should be the same as the SUPERCOP version. */
++
++ uint32x4_t diag0 = start0;
++ uint32x4_t diag1 = start1;
++ uint32x4_t diag2 = start2;
++ uint32x4_t diag3 = start3;
++
++ uint32x4_t a0;
++ uint32x4_t a1;
++ uint32x4_t a2;
++ uint32x4_t a3;
++
++ for (i = ROUNDS;i > 0;i -= 2) {
++ a0 = diag1 + diag0;
++ diag3 ^= vsriq_n_u32(vshlq_n_u32(a0,7),a0,25);
++ a1 = diag0 + diag3;
++ diag2 ^= vsriq_n_u32(vshlq_n_u32(a1,9),a1,23);
++ a2 = diag3 + diag2;
++ diag1 ^= vsriq_n_u32(vshlq_n_u32(a2,13),a2,19);
++ a3 = diag2 + diag1;
++ diag0 ^= vsriq_n_u32(vshlq_n_u32(a3,18),a3,14);
++
++ diag3 = vextq_u32(diag3,diag3,3);
++ diag2 = vextq_u32(diag2,diag2,2);
++ diag1 = vextq_u32(diag1,diag1,1);
++
++ a0 = diag3 + diag0;
++ diag1 ^= vsriq_n_u32(vshlq_n_u32(a0,7),a0,25);
++ a1 = diag0 + diag1;
++ diag2 ^= vsriq_n_u32(vshlq_n_u32(a1,9),a1,23);
++ a2 = diag1 + diag2;
++ diag3 ^= vsriq_n_u32(vshlq_n_u32(a2,13),a2,19);
++ a3 = diag2 + diag3;
++ diag0 ^= vsriq_n_u32(vshlq_n_u32(a3,18),a3,14);
++
++ diag1 = vextq_u32(diag1,diag1,3);
++ diag2 = vextq_u32(diag2,diag2,2);
++ diag3 = vextq_u32(diag3,diag3,1);
++ }
++
++ x0x5x10x15 = diag0 + start0;
++ x12x1x6x11 = diag1 + start1;
++ x8x13x2x7 = diag2 + start2;
++ x4x9x14x3 = diag3 + start3;
++
++ x0x1x10x11 = vbslq_u32(abab,x0x5x10x15,x12x1x6x11);
++ x12x13x6x7 = vbslq_u32(abab,x12x1x6x11,x8x13x2x7);
++ x8x9x2x3 = vbslq_u32(abab,x8x13x2x7,x4x9x14x3);
++ x4x5x14x15 = vbslq_u32(abab,x4x9x14x3,x0x5x10x15);
++
++ x0x1x2x3 = vcombine_u32(vget_low_u32(x0x1x10x11),vget_high_u32(x8x9x2x3));
++ x4x5x6x7 = vcombine_u32(vget_low_u32(x4x5x14x15),vget_high_u32(x12x13x6x7));
++ x8x9x10x11 = vcombine_u32(vget_low_u32(x8x9x2x3),vget_high_u32(x0x1x10x11));
++ x12x13x14x15 = vcombine_u32(vget_low_u32(x12x13x6x7),vget_high_u32(x4x5x14x15));
++
++ vst1q_u8((uint8_t *) input,(uint8x16_t) x0x1x2x3);
++ vst1q_u8(16 + (uint8_t *) input,(uint8x16_t) x4x5x6x7);
++ vst1q_u8(32 + (uint8_t *) input,(uint8x16_t) x8x9x10x11);
++ vst1q_u8(48 + (uint8_t *) input,(uint8x16_t) x12x13x14x15);
++}
+diff --git a/lib/crypto/crypto_scrypt-neon.c b/lib/crypto/crypto_scrypt-neon.c
+new file mode 100644
+index 0000000..a3bf052
+--- /dev/null
++++ b/lib/crypto/crypto_scrypt-neon.c
+@@ -0,0 +1,305 @@
++/*-
++ * Copyright 2009 Colin Percival
++ * All rights reserved.
++ *
++ * Redistribution and use in source and binary forms, with or without
++ * modification, are permitted provided that the following conditions
++ * are met:
++ * 1. Redistributions of source code must retain the above copyright
++ * notice, this list of conditions and the following disclaimer.
++ * 2. Redistributions in binary form must reproduce the above copyright
++ * notice, this list of conditions and the following disclaimer in the
++ * documentation and/or other materials provided with the distribution.
++ *
++ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
++ * SUCH DAMAGE.
++ *
++ * This file was originally written by Colin Percival as part of the Tarsnap
++ * online backup system.
++ */
++#include "scrypt_platform.h"
++
++#include <machine/cpu-features.h>
++#include <arm_neon.h>
++
++#include <errno.h>
++#include <stdint.h>
++#include <limits.h>
++#include <stdlib.h>
++#include <string.h>
++
++#ifdef USE_OPENSSL_PBKDF2
++#include <openssl/evp.h>
++#else
++#include "sha256.h"
++#endif
++#include "sysendian.h"
++
++#include "crypto_scrypt.h"
++
++#include "crypto_scrypt-neon-salsa208.h"
++
++static void blkcpy(void *, void *, size_t);
++static void blkxor(void *, void *, size_t);
++void crypto_core_salsa208_armneon2(void *);
++static void blockmix_salsa8(uint8x16_t *, uint8x16_t *, uint8x16_t *, size_t);
++static uint64_t integerify(void *, size_t);
++static void smix(uint8_t *, size_t, uint64_t, void *, void *);
++
++static void
++blkcpy(void * dest, void * src, size_t len)
++{
++ uint8x16_t * D = dest;
++ uint8x16_t * S = src;
++ size_t L = len / 16;
++ size_t i;
++
++ for (i = 0; i < L; i++)
++ D[i] = S[i];
++}
++
++static void
++blkxor(void * dest, void * src, size_t len)
++{
++ uint8x16_t * D = dest;
++ uint8x16_t * S = src;
++ size_t L = len / 16;
++ size_t i;
++
++ for (i = 0; i < L; i++)
++ D[i] = veorq_u8(D[i], S[i]);
++}
++
++/**
++ * blockmix_salsa8(B, Y, r):
++ * Compute B = BlockMix_{salsa20/8, r}(B). The input B must be 128r bytes in
++ * length; the temporary space Y must also be the same size.
++ */
++static void
++blockmix_salsa8(uint8x16_t * Bin, uint8x16_t * Bout, uint8x16_t * X, size_t r)
++{
++ size_t i;
++
++ /* 1: X <-- B_{2r - 1} */
++ blkcpy(X, &Bin[8 * r - 4], 64);
++
++ /* 2: for i = 0 to 2r - 1 do */
++ for (i = 0; i < r; i++) {
++ /* 3: X <-- H(X \xor B_i) */
++ blkxor(X, &Bin[i * 8], 64);
++ salsa20_8_intrinsic((void *) X);
++
++ /* 4: Y_i <-- X */
++ /* 6: B' <-- (Y_0, Y_2 ... Y_{2r-2}, Y_1, Y_3 ... Y_{2r-1}) */
++ blkcpy(&Bout[i * 4], X, 64);
++
++ /* 3: X <-- H(X \xor B_i) */
++ blkxor(X, &Bin[i * 8 + 4], 64);
++ salsa20_8_intrinsic((void *) X);
++
++ /* 4: Y_i <-- X */
++ /* 6: B' <-- (Y_0, Y_2 ... Y_{2r-2}, Y_1, Y_3 ... Y_{2r-1}) */
++ blkcpy(&Bout[(r + i) * 4], X, 64);
++ }
++}
++
++/**
++ * integerify(B, r):
++ * Return the result of parsing B_{2r-1} as a little-endian integer.
++ */
++static uint64_t
++integerify(void * B, size_t r)
++{
++ uint8_t * X = (void*)((uintptr_t)(B) + (2 * r - 1) * 64);
++
++ return (le64dec(X));
++}
++
++/**
++ * smix(B, r, N, V, XY):
++ * Compute B = SMix_r(B, N). The input B must be 128r bytes in length; the
++ * temporary storage V must be 128rN bytes in length; the temporary storage
++ * XY must be 256r bytes in length. The value N must be a power of 2.
++ */
++static void
++smix(uint8_t * B, size_t r, uint64_t N, void * V, void * XY)
++{
++ uint8x16_t * X = XY;
++ uint8x16_t * Y = (void *)((uintptr_t)(XY) + 128 * r);
++ uint8x16_t * Z = (void *)((uintptr_t)(XY) + 256 * r);
++ uint32_t * X32 = (void *)X;
++ uint64_t i, j;
++ size_t k;
++
++ /* 1: X <-- B */
++ blkcpy(X, B, 128 * r);
++
++ /* 2: for i = 0 to N - 1 do */
++ for (i = 0; i < N; i += 2) {
++ /* 3: V_i <-- X */
++ blkcpy((void *)((uintptr_t)(V) + i * 128 * r), X, 128 * r);
++
++ /* 4: X <-- H(X) */
++ blockmix_salsa8(X, Y, Z, r);
++
++ /* 3: V_i <-- X */
++ blkcpy((void *)((uintptr_t)(V) + (i + 1) * 128 * r),
++ Y, 128 * r);
++
++ /* 4: X <-- H(X) */
++ blockmix_salsa8(Y, X, Z, r);
++ }
++
++ /* 6: for i = 0 to N - 1 do */
++ for (i = 0; i < N; i += 2) {
++ /* 7: j <-- Integerify(X) mod N */
++ j = integerify(X, r) & (N - 1);
++
++ /* 8: X <-- H(X \xor V_j) */
++ blkxor(X, (void *)((uintptr_t)(V) + j * 128 * r), 128 * r);
++ blockmix_salsa8(X, Y, Z, r);
++
++ /* 7: j <-- Integerify(X) mod N */
++ j = integerify(Y, r) & (N - 1);
++
++ /* 8: X <-- H(X \xor V_j) */
++ blkxor(Y, (void *)((uintptr_t)(V) + j * 128 * r), 128 * r);
++ blockmix_salsa8(Y, X, Z, r);
++ }
++
++ /* 10: B' <-- X */
++ blkcpy(B, X, 128 * r);
++}
++
++/**
++ * crypto_scrypt(passwd, passwdlen, salt, saltlen, N, r, p, buf, buflen):
++ * Compute scrypt(passwd[0 .. passwdlen - 1], salt[0 .. saltlen - 1], N, r,
++ * p, buflen) and write the result into buf. The parameters r, p, and buflen
++ * must satisfy r * p < 2^30 and buflen <= (2^32 - 1) * 32. The parameter N
++ * must be a power of 2.
++ *
++ * Return 0 on success; or -1 on error.
++ */
++int
++crypto_scrypt(const uint8_t * passwd, size_t passwdlen,
++ const uint8_t * salt, size_t saltlen, uint64_t N, uint32_t r, uint32_t p,
++ uint8_t * buf, size_t buflen)
++{
++ void * B0, * V0, * XY0;
++ uint8_t * B;
++ uint32_t * V;
++ uint32_t * XY;
++ uint32_t i;
++
++ /* Sanity-check parameters. */
++#if SIZE_MAX > UINT32_MAX
++ if (buflen > (((uint64_t)(1) << 32) - 1) * 32) {
++ errno = EFBIG;
++ goto err0;
++ }
++#endif
++ if ((uint64_t)(r) * (uint64_t)(p) >= (1 << 30)) {
++ errno = EFBIG;
++ goto err0;
++ }
++ if (((N & (N - 1)) != 0) || (N == 0)) {
++ errno = EINVAL;
++ goto err0;
++ }
++ if ((r > SIZE_MAX / 128 / p) ||
++#if SIZE_MAX / 256 <= UINT32_MAX
++ (r > SIZE_MAX / 256) ||
++#endif
++ (N > SIZE_MAX / 128 / r)) {
++ errno = ENOMEM;
++ goto err0;
++ }
++
++ /* Allocate memory. */
++#ifdef HAVE_POSIX_MEMALIGN
++ if ((errno = posix_memalign(&B0, 64, 128 * r * p)) != 0)
++ goto err0;
++ B = (uint8_t *)(B0);
++ if ((errno = posix_memalign(&XY0, 64, 256 * r + 64)) != 0)
++ goto err1;
++ XY = (uint32_t *)(XY0);
++#ifndef MAP_ANON
++ if ((errno = posix_memalign(&V0, 64, 128 * r * N)) != 0)
++ goto err2;
++ V = (uint32_t *)(V0);
++#endif
++#else
++ if ((B0 = malloc(128 * r * p + 63)) == NULL)
++ goto err0;
++ B = (uint8_t *)(((uintptr_t)(B0) + 63) & ~ (uintptr_t)(63));
++ if ((XY0 = malloc(256 * r + 64 + 63)) == NULL)
++ goto err1;
++ XY = (uint32_t *)(((uintptr_t)(XY0) + 63) & ~ (uintptr_t)(63));
++#ifndef MAP_ANON
++ if ((V0 = malloc(128 * r * N + 63)) == NULL)
++ goto err2;
++ V = (uint32_t *)(((uintptr_t)(V0) + 63) & ~ (uintptr_t)(63));
++#endif
++#endif
++#ifdef MAP_ANON
++ if ((V0 = mmap(NULL, 128 * r * N, PROT_READ | PROT_WRITE,
++#ifdef MAP_NOCORE
++ MAP_ANON | MAP_PRIVATE | MAP_NOCORE,
++#else
++ MAP_ANON | MAP_PRIVATE,
++#endif
++ -1, 0)) == MAP_FAILED)
++ goto err2;
++ V = (uint32_t *)(V0);
++#endif
++
++ /* 1: (B_0 ... B_{p-1}) <-- PBKDF2(P, S, 1, p * MFLen) */
++#ifdef USE_OPENSSL_PBKDF2
++ PKCS5_PBKDF2_HMAC((const char *)passwd, passwdlen, salt, saltlen, 1, EVP_sha256(), p * 128 * r, B);
++#else
++ PBKDF2_SHA256(passwd, passwdlen, salt, saltlen, 1, B, p * 128 * r);
++#endif
++
++ /* 2: for i = 0 to p - 1 do */
++ for (i = 0; i < p; i++) {
++ /* 3: B_i <-- MF(B_i, N) */
++ smix(&B[i * 128 * r], r, N, V, XY);
++ }
++
++ /* 5: DK <-- PBKDF2(P, B, 1, dkLen) */
++#ifdef USE_OPENSSL_PBKDF2
++ PKCS5_PBKDF2_HMAC((const char *)passwd, passwdlen, B, p * 128 * r, 1, EVP_sha256(), buflen, buf);
++#else
++ PBKDF2_SHA256(passwd, passwdlen, B, p * 128 * r, 1, buf, buflen);
++#endif
++
++ /* Free memory. */
++#ifdef MAP_ANON
++ if (munmap(V0, 128 * r * N))
++ goto err2;
++#else
++ free(V0);
++#endif
++ free(XY0);
++ free(B0);
++
++ /* Success! */
++ return (0);
++
++err2:
++ free(XY0);
++err1:
++ free(B0);
++err0:
++ /* Failure! */
++ return (-1);
++}
diff --git a/crypto/scrypt/patches/use_openssl_pbkdf2.patch b/crypto/scrypt/patches/use_openssl_pbkdf2.patch
new file mode 100644
index 000000000..0a1328cc0
--- /dev/null
+++ b/crypto/scrypt/patches/use_openssl_pbkdf2.patch
@@ -0,0 +1,80 @@
+diff --git a/lib/crypto/crypto_scrypt-ref.c b/lib/crypto/crypto_scrypt-ref.c
+index 79a6f8f..60ef2aa 100644
+--- a/lib/crypto/crypto_scrypt-ref.c
++++ b/lib/crypto/crypto_scrypt-ref.c
+@@ -34,7 +34,11 @@
+ #include <stdlib.h>
+ #include <string.h>
+
++#ifdef USE_OPENSSL_PBKDF2
++#include <openssl/evp.h>
++#else
+ #include "sha256.h"
++#endif
+ #include "sysendian.h"
+
+ #include "crypto_scrypt.h"
+@@ -256,7 +260,11 @@ crypto_scrypt(const uint8_t * passwd, size_t passwdlen,
+ goto err2;
+
+ /* 1: (B_0 ... B_{p-1}) <-- PBKDF2(P, S, 1, p * MFLen) */
++#ifdef USE_OPENSSL_PBKDF2
++ PKCS5_PBKDF2_HMAC((const char *)passwd, passwdlen, salt, saltlen, 1, EVP_sha256(), p * 128 * r, B);
++#else
+ PBKDF2_SHA256(passwd, passwdlen, salt, saltlen, 1, B, p * 128 * r);
++#endif
+
+ /* 2: for i = 0 to p - 1 do */
+ for (i = 0; i < p; i++) {
+@@ -265,7 +273,11 @@ crypto_scrypt(const uint8_t * passwd, size_t passwdlen,
+ }
+
+ /* 5: DK <-- PBKDF2(P, B, 1, dkLen) */
++#ifdef USE_OPENSSL_PBKDF2
++ PKCS5_PBKDF2_HMAC((const char *)passwd, passwdlen, B, p * 128 * r, 1, EVP_sha256(), buflen, buf);
++#else
+ PBKDF2_SHA256(passwd, passwdlen, B, p * 128 * r, 1, buf, buflen);
++#endif
+
+ /* Free memory. */
+ free(V);
+diff --git a/lib/crypto/crypto_scrypt-sse.c b/lib/crypto/crypto_scrypt-sse.c
+index 875175e..dd18f29 100644
+--- a/lib/crypto/crypto_scrypt-sse.c
++++ b/lib/crypto/crypto_scrypt-sse.c
+@@ -37,7 +37,11 @@
+ #include <stdlib.h>
+ #include <string.h>
+
++#ifdef USE_OPENSSL_PBKDF2
++#include <openssl/evp.h>
++#else
+ #include "sha256.h"
++#endif
+ #include "sysendian.h"
+
+ #include "crypto_scrypt.h"
+@@ -332,7 +336,11 @@ crypto_scrypt(const uint8_t * passwd, size_t passwdlen,
+ #endif
+
+ /* 1: (B_0 ... B_{p-1}) <-- PBKDF2(P, S, 1, p * MFLen) */
++#ifdef USE_OPENSSL_PBKDF2
++ PKCS5_PBKDF2_HMAC((const char *)passwd, passwdlen, salt, saltlen, 1, EVP_sha256(), p * 128 * r, B);
++#else
+ PBKDF2_SHA256(passwd, passwdlen, salt, saltlen, 1, B, p * 128 * r);
++#endif
+
+ /* 2: for i = 0 to p - 1 do */
+ for (i = 0; i < p; i++) {
+@@ -341,7 +349,11 @@ crypto_scrypt(const uint8_t * passwd, size_t passwdlen,
+ }
+
+ /* 5: DK <-- PBKDF2(P, B, 1, dkLen) */
++#ifdef USE_OPENSSL_PBKDF2
++ PKCS5_PBKDF2_HMAC((const char *)passwd, passwdlen, B, p * 128 * r, 1, EVP_sha256(), buflen, buf);
++#else
+ PBKDF2_SHA256(passwd, passwdlen, B, p * 128 * r, 1, buf, buflen);
++#endif
+
+ /* Free memory. */
+ #ifdef MAP_ANON
diff --git a/crypto/scrypt/scrypt.config b/crypto/scrypt/scrypt.config
new file mode 100644
index 000000000..3ccb4d0eb
--- /dev/null
+++ b/crypto/scrypt/scrypt.config
@@ -0,0 +1,94 @@
+CONFIGURE_ARGS="\
+ \
+"
+
+# unneeded directories
+UNNEEDED_SOURCES="\
+lib/scryptenc \
+"
+
+# unneeded files
+UNNEEDED_SOURCES+="\
+config.h.in \
+configure \
+FORMAT \
+main.c \
+Makefile.in \
+scrypt.1 \
+lib/crypto/crypto_aesctr.c \
+lib/crypto/crypto_aesctr.h \
+lib/crypto/crypto_scrypt-nosse.c \
+lib/crypto/sha256.c \
+lib/crypto/sha256.h \
+lib/util/memlimit.c \
+lib/util/memlimit.h \
+lib/util/readpass.c \
+lib/util/readpass.h \
+lib/util/warn.c \
+lib/util/warn.h \
+"
+
+NEEDED_SOURCES="\
+config.h \
+lib \
+scrypt_platform.h \
+"
+
+SCRYPT_INCLUDES="\
+lib/crypto \
+lib/util \
+"
+
+SCRYPT_SOURCES="\
+lib/crypto/crypto_scrypt-ref.c \
+"
+
+SCRYPT_SOURCES_arm="\
+"
+
+SCRYPT_SOURCES_EXCLUDES_arm="\
+"
+
+SCRYPT_SOURCES_arm_neon="\
+lib/crypto/crypto_scrypt-neon.c \
+"
+
+SCRYPT_SOURCES_EXCLUDES_arm_neon="\
+lib/crypto/crypto_scrypt-ref.c \
+"
+
+SCRYPT_SOURCES_mips="\
+"
+
+SCRYPT_SOURCES_EXCLUDES_mips="\
+"
+
+SCRYPT_SOURCES_x86="\
+lib/crypto/crypto_scrypt-sse.c \
+"
+
+SCRYPT_SOURCES_EXCLUDES_x86="\
+lib/crypto/crypto_scrypt-ref.c \
+"
+
+SCRYPT_SOURCES_x86_64="\
+lib/crypto/crypto_scrypt-sse.c \
+"
+
+SCRYPT_SOURCES_EXCLUDES_x86_64="\
+lib/crypto/crypto_scrypt-ref.c \
+"
+
+SCRYPT_PATCHES="\
+use_openssl_pbkdf2.patch \
+arm-neon.patch \
+"
+
+SCRYPT_PATCHES_use_openssl_pbkdf2_SOURCES="\
+lib/crypto/crypto_scrypt-ref.c \
+"
+
+SCRYPT_PATCHES_bionic_SOURCES="\
+lib/crypto/crypto_scrypt-neon.c \
+lib/crypto/crypto_scrypt-neon-salsa208.h \
+"
diff --git a/crypto/scrypt/scrypt.version b/crypto/scrypt/scrypt.version
new file mode 100644
index 000000000..155e26061
--- /dev/null
+++ b/crypto/scrypt/scrypt.version
@@ -0,0 +1 @@
+SCRYPT_VERSION=1.1.6
diff --git a/crypto/scrypt/scrypt_platform.h b/crypto/scrypt/scrypt_platform.h
new file mode 100644
index 000000000..5cec23631
--- /dev/null
+++ b/crypto/scrypt/scrypt_platform.h
@@ -0,0 +1,12 @@
+#ifndef _SCRYPT_PLATFORM_H_
+#define _SCRYPT_PLATFORM_H_
+
+#if defined(CONFIG_H_FILE)
+#include CONFIG_H_FILE
+#elif defined(HAVE_CONFIG_H)
+#include "config.h"
+#else
+#error Need either CONFIG_H_FILE or HAVE_CONFIG_H defined.
+#endif
+
+#endif /* !_SCRYPT_PLATFORM_H_ */
diff --git a/crypto/scrypt/tests/Android.mk b/crypto/scrypt/tests/Android.mk
new file mode 100644
index 000000000..c20b41da9
--- /dev/null
+++ b/crypto/scrypt/tests/Android.mk
@@ -0,0 +1,26 @@
+# Build the scrypt unit tests
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+
+LOCAL_SRC_FILES:= \
+ scrypt_test.cpp
+
+LOCAL_C_INCLUDES := \
+ external/gtest/include \
+ external/scrypt/lib/crypto
+
+LOCAL_SHARED_LIBRARIES := \
+ libcrypto
+
+LOCAL_STATIC_LIBRARIES := \
+ libscrypt_static \
+ libgtest \
+ libgtest_main
+
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := scrypttwrp_test
+
+include $(BUILD_NATIVE_TEST)
diff --git a/crypto/scrypt/tests/scrypt_test.cpp b/crypto/scrypt/tests/scrypt_test.cpp
new file mode 100644
index 000000000..ffb568df9
--- /dev/null
+++ b/crypto/scrypt/tests/scrypt_test.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "scrypt_test"
+#include <UniquePtr.h>
+#include <utils/Log.h>
+
+#include <gtest/gtest.h>
+
+#include <fstream>
+#include <iostream>
+
+extern "C" {
+#include <crypto_scrypt.h>
+}
+
+namespace android {
+
+typedef struct scrypt_test_setting_t {
+ const char *pw, *salt;
+ uint32_t Nfactor, rfactor, pfactor;
+} scrypt_test_setting;
+
+static const scrypt_test_setting post_settings[] = {
+ {"", "", 16, 1, 1},
+ {"password", "NaCl", 1024, 8, 16},
+ {"pleaseletmein", "SodiumChloride", 16384, 8, 1},
+ {0, 0, 0, 0, 0}
+};
+
+static const uint8_t post_vectors[][64] = {
+ {0x77,0xd6,0x57,0x62,0x38,0x65,0x7b,0x20,0x3b,0x19,0xca,0x42,0xc1,0x8a,0x04,0x97,
+ 0xf1,0x6b,0x48,0x44,0xe3,0x07,0x4a,0xe8,0xdf,0xdf,0xfa,0x3f,0xed,0xe2,0x14,0x42,
+ 0xfc,0xd0,0x06,0x9d,0xed,0x09,0x48,0xf8,0x32,0x6a,0x75,0x3a,0x0f,0xc8,0x1f,0x17,
+ 0xe8,0xd3,0xe0,0xfb,0x2e,0x0d,0x36,0x28,0xcf,0x35,0xe2,0x0c,0x38,0xd1,0x89,0x06},
+ {0xfd,0xba,0xbe,0x1c,0x9d,0x34,0x72,0x00,0x78,0x56,0xe7,0x19,0x0d,0x01,0xe9,0xfe,
+ 0x7c,0x6a,0xd7,0xcb,0xc8,0x23,0x78,0x30,0xe7,0x73,0x76,0x63,0x4b,0x37,0x31,0x62,
+ 0x2e,0xaf,0x30,0xd9,0x2e,0x22,0xa3,0x88,0x6f,0xf1,0x09,0x27,0x9d,0x98,0x30,0xda,
+ 0xc7,0x27,0xaf,0xb9,0x4a,0x83,0xee,0x6d,0x83,0x60,0xcb,0xdf,0xa2,0xcc,0x06,0x40},
+ {0x70,0x23,0xbd,0xcb,0x3a,0xfd,0x73,0x48,0x46,0x1c,0x06,0xcd,0x81,0xfd,0x38,0xeb,
+ 0xfd,0xa8,0xfb,0xba,0x90,0x4f,0x8e,0x3e,0xa9,0xb5,0x43,0xf6,0x54,0x5d,0xa1,0xf2,
+ 0xd5,0x43,0x29,0x55,0x61,0x3f,0x0f,0xcf,0x62,0xd4,0x97,0x05,0x24,0x2a,0x9a,0xf9,
+ 0xe6,0x1e,0x85,0xdc,0x0d,0x65,0x1e,0x40,0xdf,0xcf,0x01,0x7b,0x45,0x57,0x58,0x87},
+};
+
+class ScryptTest : public ::testing::Test {
+};
+
+TEST_F(ScryptTest, TestVectors) {
+ int i;
+
+ for (i = 0; post_settings[i].pw != NULL; i++) {
+ uint8_t output[64];
+
+ scrypt_test_setting_t s = post_settings[i];
+ ASSERT_EQ(0,
+ crypto_scrypt((const uint8_t*) s.pw, strlen(s.pw), (const uint8_t*) s.salt,
+ strlen(s.salt), s.Nfactor, s.rfactor, s.pfactor, output, sizeof(output)))
+ << "scrypt call should succeed for " << i << "; error=" << strerror(errno);
+ ASSERT_EQ(0, memcmp(post_vectors[i], output, sizeof(output)))
+ << "Should match expected output";
+ }
+}
+
+}
diff --git a/crypto/vold_decrypt/Android.mk b/crypto/vold_decrypt/Android.mk
new file mode 100644
index 000000000..ddfad0327
--- /dev/null
+++ b/crypto/vold_decrypt/Android.mk
@@ -0,0 +1,99 @@
+# Copyright (C) 2017 TeamWin Recovery Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+ifeq ($(TW_INCLUDE_CRYPTO), true)
+ ifneq ($(TW_CRYPTO_USE_SYSTEM_VOLD),)
+
+
+ # Parse TW_CRYPTO_USE_SYSTEM_VOLD
+ ifeq ($(TW_CRYPTO_USE_SYSTEM_VOLD),true)
+ # Just enabled, so only vold + vdc
+ services :=
+ else
+ # Additional services needed by vold
+ services := $(TW_CRYPTO_USE_SYSTEM_VOLD)
+ endif
+
+ # List of .rc files for each additional service
+ rc_files := $(foreach item,$(services),init.recovery.vold_decrypt.$(item).rc)
+
+
+ include $(CLEAR_VARS)
+ LOCAL_MODULE := init.recovery.vold_decrypt.rc
+ LOCAL_MODULE_TAGS := eng
+ LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES
+
+ # Cannot send to TARGET_RECOVERY_ROOT_OUT since build system wipes init*.rc
+ # during ramdisk creation and only allows init.recovery.*.rc files to be copied
+ # from TARGET_ROOT_OUT thereafter
+ LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)
+
+ LOCAL_SRC_FILES := $(LOCAL_MODULE)
+
+ # Add additional .rc files and imports into init.recovery.vold_decrypt.rc
+ # Note: any init.recovery.vold_decrypt.{service}.rc that are not default
+ # in crypto/vold_decrypt should be in the device tree
+ LOCAL_POST_INSTALL_CMD := $(hide) \
+ $(foreach item, $(rc_files), \
+ sed -i '1iimport \/$(item)' "$(TARGET_ROOT_OUT)/$(LOCAL_MODULE)"; \
+ if [ -f "$(LOCAL_PATH)/$(item)" ]; then \
+ cp -f "$(LOCAL_PATH)/$(item)" "$(TARGET_ROOT_OUT)"/; \
+ fi; \
+ )
+
+ ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 26; echo $$?),0)
+ # Truncate service_name to max 16 characters
+ LOCAL_POST_INSTALL_CMD += \
+ $(foreach item, $(rc_files), \
+ if [ -f "$(TARGET_ROOT_OUT)/$(item)" ]; then \
+ sed -i 's/\([ \t]*service[ \t]*\)\(.\{16\}\).*\([ \t].*\)/\1\2\3/' "$(TARGET_ROOT_OUT)/$(item)"; \
+ fi; \
+ )
+ endif
+
+ include $(BUILD_PREBUILT)
+
+
+ include $(CLEAR_VARS)
+ LOCAL_MODULE := libvolddecrypt
+ LOCAL_MODULE_TAGS := eng optional
+ LOCAL_CFLAGS := -Wall
+ ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 23; echo $$?),0)
+ LOCAL_C_INCLUDES += external/stlport/stlport bionic bionic/libstdc++/include
+ endif
+
+ ifneq ($(services),)
+ ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 26; echo $$?),0)
+ # Truncate service_name to max 12 characters due to the 4 character prefix
+ truncated_services := $(foreach item,$(services),$(shell echo -n "$(item)" | sed 's/\(.\{12\}\).*/\1/'))
+ LOCAL_CFLAGS += -DTW_CRYPTO_SYSTEM_VOLD_SERVICES='"$(truncated_services)"'
+ LOCAL_CFLAGS += -D_USING_SHORT_SERVICE_NAMES
+ else
+ LOCAL_CFLAGS += -DTW_CRYPTO_SYSTEM_VOLD_SERVICES='"$(services)"'
+ endif
+ endif
+
+ ifeq ($(TW_CRYPTO_SYSTEM_VOLD_DEBUG),true)
+ # Enabling strace will expose the password in the strace logs!!
+ LOCAL_CFLAGS += -DTW_CRYPTO_SYSTEM_VOLD_DEBUG
+ endif
+
+ LOCAL_SRC_FILES = vold_decrypt.cpp
+ LOCAL_SHARED_LIBRARIES := libcutils
+ include $(BUILD_STATIC_LIBRARY)
+
+ endif
+endif
diff --git a/crypto/vold_decrypt/init.recovery.vold_decrypt.hwservicemanager.rc b/crypto/vold_decrypt/init.recovery.vold_decrypt.hwservicemanager.rc
new file mode 100755
index 000000000..ab8b4ac4d
--- /dev/null
+++ b/crypto/vold_decrypt/init.recovery.vold_decrypt.hwservicemanager.rc
@@ -0,0 +1,18 @@
+# Service names must be less than 16 characters in android-7.1 and
+# below. The makefile will truncate service names if needed in any
+# init.recovery.vold_decryp.*.rc file found in the vold_decrypt
+# directory.
+# It cannot however do this for any .rc file(s) that may be
+# overridden by the device tree files!
+
+# The seclabels are not needed when built in Android 8.0 tree
+# in 7.1 however the below do not exist, so run them under vold
+service sys_hwservicemanager /system/bin/hwservicemanager
+ user root
+ group root
+ setenv PATH /system/bin
+ setenv LD_LIBRARY_PATH /system/lib64:/system/lib
+ onrestart setprop hwservicemanager.ready false
+ disabled
+ oneshot
+ seclabel u:r:vold:s0
diff --git a/crypto/vold_decrypt/init.recovery.vold_decrypt.keymaster-3-0.rc b/crypto/vold_decrypt/init.recovery.vold_decrypt.keymaster-3-0.rc
new file mode 100755
index 000000000..e9f0b0241
--- /dev/null
+++ b/crypto/vold_decrypt/init.recovery.vold_decrypt.keymaster-3-0.rc
@@ -0,0 +1,17 @@
+# Service names must be less than 16 characters in android-7.1 and
+# below. The makefile will truncate service names if needed in any
+# init.recovery.vold_decryp.*.rc file found in the vold_decrypt
+# directory.
+# It cannot however do this for any .rc file(s) that may be
+# overridden by the device tree files!
+
+# The seclabels are not needed when built in Android 8.0 tree
+# in 7.1 however the below do not exist, so run them under vold
+service ven_keymaster-3-0 /vendor/bin/hw/android.hardware.keymaster@3.0-service
+ user root
+ group root
+ setenv PATH /vendor/bin:/system/bin
+ setenv LD_LIBRARY_PATH /vendor/lib64:/system/lib64:/vendor/lib:/system/lib
+ disabled
+ oneshot
+ seclabel u:r:vold:s0
diff --git a/crypto/vold_decrypt/init.recovery.vold_decrypt.qseecomd.rc b/crypto/vold_decrypt/init.recovery.vold_decrypt.qseecomd.rc
new file mode 100755
index 000000000..08666085d
--- /dev/null
+++ b/crypto/vold_decrypt/init.recovery.vold_decrypt.qseecomd.rc
@@ -0,0 +1,26 @@
+on fs
+ # needed to make qseecomd work in recovery
+ chmod 0660 /dev/qseecom
+ chown system drmrpc /dev/qseecom
+ chmod 0664 /dev/ion
+ chown system system /dev/ion
+
+
+# Oreo has qseecomd in /vendor/bin so add the additional
+# service. Only an existing binary will be started, never both.
+
+service sys_qseecomd /system/bin/qseecomd
+ user root
+ group root
+ setenv PATH /system/bin
+ setenv LD_LIBRARY_PATH /system/lib64:/system/lib
+ disabled
+ oneshot
+
+service ven_qseecomd /vendor/bin/qseecomd
+ user root
+ group root
+ setenv PATH /vendor/bin:/system/bin
+ setenv LD_LIBRARY_PATH /vendor/lib64:/system/lib64:/vendor/lib:/system/lib
+ disabled
+ oneshot
diff --git a/crypto/vold_decrypt/init.recovery.vold_decrypt.rc b/crypto/vold_decrypt/init.recovery.vold_decrypt.rc
new file mode 100755
index 000000000..65983eb25
--- /dev/null
+++ b/crypto/vold_decrypt/init.recovery.vold_decrypt.rc
@@ -0,0 +1,10 @@
+
+service sys_vold /system/bin/vold \
+ --blkid_context=u:r:blkid:s0 --blkid_untrusted_context=u:r:blkid_untrusted:s0 \
+ --fsck_context=u:r:fsck:s0 --fsck_untrusted_context=u:r:fsck_untrusted:s0
+ socket vold stream 0660 root mount
+ socket cryptd stream 0660 root mount
+ setenv PATH /system/bin
+ setenv LD_LIBRARY_PATH /system/lib64:/system/lib
+ disabled
+ oneshot
diff --git a/crypto/vold_decrypt/vold_decrypt.cpp b/crypto/vold_decrypt/vold_decrypt.cpp
new file mode 100644
index 000000000..f6e6e3482
--- /dev/null
+++ b/crypto/vold_decrypt/vold_decrypt.cpp
@@ -0,0 +1,1126 @@
+/*
+ Copyright 2017 TeamWin
+ This file is part of TWRP/TeamWin Recovery Project.
+
+ TWRP is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ TWRP is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with TWRP. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/mount.h>
+#include <sys/time.h>
+#include <dirent.h>
+#include <fnmatch.h>
+
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <fstream>
+#include <string>
+#include <vector>
+#include <sstream>
+
+#ifdef _USING_SHORT_SERVICE_NAMES
+#include <map>
+#endif
+
+#include "../../partitions.hpp"
+#include "../../twrp-functions.hpp"
+
+using namespace std;
+
+extern "C" {
+ #include <cutils/properties.h>
+}
+
+#include "vold_decrypt.h"
+
+namespace {
+
+/* Timeouts as defined by ServiceManager */
+
+/* The maximum amount of time to wait for a service to start or stop,
+ * in micro-seconds (really an approximation) */
+#define SLEEP_MAX_USEC 2000000 /* 2 seconds */
+/* The minimal sleeping interval between checking for the service's state
+ * when looping for SLEEP_MAX_USEC */
+#define SLEEP_MIN_USEC 200000 /* 200 msec */
+
+
+/* vold response codes defined in ResponseCode.h */
+// 200 series - Requested action has been successfully completed
+#define COMMAND_OKAY 200
+#define PASSWORD_TYPE_RESULT 213
+
+
+#define LOGINFO(...) do { printf(__VA_ARGS__); if (fp_kmsg) { fprintf(fp_kmsg, "[VOLD_DECRYPT]I:" __VA_ARGS__); fflush(fp_kmsg); } } while (0)
+#define LOGKMSG(...) do { if (fp_kmsg) { fprintf(fp_kmsg, "[VOLD_DECRYPT]K:" __VA_ARGS__); fflush(fp_kmsg); } } while (0)
+#define LOGERROR(...) do { printf(__VA_ARGS__); if (fp_kmsg) { fprintf(fp_kmsg, "[VOLD_DECRYPT]E:" __VA_ARGS__); fflush(fp_kmsg); } } while (0)
+
+FILE *fp_kmsg = NULL;
+
+
+/* Debugging Functions */
+#ifdef TW_CRYPTO_SYSTEM_VOLD_DEBUG
+
+#ifndef VD_STRACE_BIN
+#define VD_STRACE_BIN "/sbin/strace"
+#endif
+
+bool has_strace = false;
+pid_t pid_strace = 0;
+
+void Strace_init_Start(void) {
+ has_strace = TWFunc::Path_Exists(VD_STRACE_BIN);
+ if (!has_strace) {
+ LOGINFO("strace binary (%s) not found, disabling strace in vold_decrypt!\n", VD_STRACE_BIN);
+ return;
+ }
+
+ pid_t pid;
+ switch(pid = fork())
+ {
+ case -1:
+ LOGKMSG("forking strace_init failed: %d (%s)!\n", errno, strerror(errno));
+ return;
+ case 0: // child
+ execl(VD_STRACE_BIN, "strace", "-q", "-tt", "-ff", "-v", "-y", "-s", "1000", "-o", "/tmp/strace_init.log", "-p", "1" , NULL);
+ LOGKMSG("strace_init fork failed: %d (%s)!\n", errno, strerror(errno));
+ exit(-1);
+ default:
+ LOGKMSG("Starting strace_init (pid=%d)\n", pid);
+ pid_strace = pid;
+ return;
+ }
+}
+
+void Strace_init_Stop(void) {
+ if (pid_strace > 0) {
+ LOGKMSG("Stopping strace_init (pid=%d)\n", pid_strace);
+ int timeout;
+ int status;
+ pid_t retpid = waitpid(pid_strace, &status, WNOHANG);
+
+ kill(pid_strace, SIGTERM);
+ for (timeout = 5; retpid == 0 && timeout; --timeout) {
+ sleep(1);
+ retpid = waitpid(pid_strace, &status, WNOHANG);
+ }
+ if (retpid)
+ LOGKMSG("strace_init terminated successfully\n");
+ else {
+ // SIGTERM didn't work, kill it instead
+ kill(pid_strace, SIGKILL);
+ for (timeout = 5; retpid == 0 && timeout; --timeout) {
+ sleep(1);
+ retpid = waitpid(pid_strace, &status, WNOHANG);
+ }
+ if (retpid)
+ LOGKMSG("strace_init killed successfully\n");
+ else
+ LOGKMSG("strace_init took too long to kill, may be a zombie process\n");
+ }
+ }
+}
+#endif // TW_CRYPTO_SYSTEM_VOLD_DEBUG
+
+
+/* Convert a binary key of specified length into an ascii hex string equivalent,
+ * without the leading 0x and with null termination
+ *
+ * Original code from cryptfs.c
+ */
+string convert_key_to_hex_ascii(const string& master_key) {
+ size_t i;
+ unsigned char nibble;
+ string master_key_ascii = "";
+
+ for (i = 0; i < master_key.size(); ++i) {
+ nibble = (master_key[i] >> 4) & 0xf;
+ nibble += nibble > 9 ? 0x57 : 0x30;
+ master_key_ascii += nibble;
+
+ nibble = master_key[i] & 0xf;
+ nibble += nibble > 9 ? 0x57 : 0x30;
+ master_key_ascii += nibble;
+ }
+
+ return master_key_ascii;
+}
+
+/* Helper Functions */
+#define PATH_EXISTS(path) (access(path, F_OK) >= 0)
+
+int vrename(const string& oldname, const string& newname, bool verbose = false) {
+ const char *old_name = oldname.c_str();
+ const char *new_name = newname.c_str();
+
+ if (!PATH_EXISTS(old_name))
+ return 0;
+
+ if (rename(old_name, new_name) < 0) {
+ LOGERROR("Moving %s to %s failed: %d (%s)\n", old_name, new_name, errno, strerror(errno));
+ return -1;
+ } else if (verbose)
+ LOGINFO("Renamed %s to %s\n", old_name, new_name);
+ else
+ LOGKMSG("Renamed %s to %s\n", old_name, new_name);
+ return 0;
+}
+
+int vsymlink(const string& oldname, const string& newname, bool verbose = false) {
+ const char *old_name = oldname.c_str();
+ const char *new_name = newname.c_str();
+
+ if (!PATH_EXISTS(old_name))
+ return 0;
+
+ if (symlink(old_name, new_name) < 0) {
+ LOGERROR("Symlink %s -> %s failed: %d (%s)\n", new_name, old_name, errno, strerror(errno));
+ return -1;
+ } else if (verbose)
+ LOGINFO("Symlinked %s -> %s\n", new_name, old_name);
+ else
+ LOGKMSG("Symlinked %s -> %s\n", new_name, old_name);
+ return 0;
+}
+
+
+/* Properties and Services Functions */
+string Wait_For_Property(const string& property_name, int utimeout = SLEEP_MAX_USEC, const string& expected_value = "not_empty") {
+ char prop_value[PROPERTY_VALUE_MAX];
+
+ if (expected_value == "not_empty") {
+ while (utimeout > 0) {
+ property_get(property_name.c_str(), prop_value, "error");
+ if (strcmp(prop_value, "error") != 0)
+ break;
+ LOGKMSG("waiting for %s to get set\n", property_name.c_str());
+ utimeout -= SLEEP_MIN_USEC;
+ usleep(SLEEP_MIN_USEC);;
+ }
+ }
+ else {
+ while (utimeout > 0) {
+ property_get(property_name.c_str(), prop_value, "error");
+ if (strcmp(prop_value, expected_value.c_str()) == 0)
+ break;
+ LOGKMSG("waiting for %s to change from '%s' to '%s'\n", property_name.c_str(), prop_value, expected_value.c_str());
+ utimeout -= SLEEP_MIN_USEC;
+ usleep(SLEEP_MIN_USEC);;
+ }
+ }
+ property_get(property_name.c_str(), prop_value, "error");
+
+ return prop_value;
+}
+
+string Get_Service_State(const string& initrc_svc) {
+ char prop_value[PROPERTY_VALUE_MAX];
+ string init_svc = "init.svc." + initrc_svc;
+ property_get(init_svc.c_str(), prop_value, "error");
+ return prop_value;
+}
+
+bool Service_Exists(const string& initrc_svc) {
+ return (Get_Service_State(initrc_svc) != "error");
+}
+
+bool Is_Service_Running(const string& initrc_svc) {
+ return (Get_Service_State(initrc_svc) == "running");
+}
+
+bool Is_Service_Stopped(const string& initrc_svc) {
+ return (Get_Service_State(initrc_svc) == "stopped");
+}
+
+bool Start_Service(const string& initrc_svc, int utimeout = SLEEP_MAX_USEC) {
+ string res = "error";
+ string init_svc = "init.svc." + initrc_svc;
+
+ property_set("ctl.start", initrc_svc.c_str());
+
+ res = Wait_For_Property(init_svc, utimeout, "running");
+
+ LOGINFO("Start service %s: %s.\n", initrc_svc.c_str(), res.c_str());
+
+ return (res == "running");
+}
+
+bool Stop_Service(const string& initrc_svc, int utimeout = SLEEP_MAX_USEC) {
+ string res = "error";
+
+ if (Service_Exists(initrc_svc)) {
+ string init_svc = "init.svc." + initrc_svc;
+ property_set("ctl.stop", initrc_svc.c_str());
+ res = Wait_For_Property(init_svc, utimeout, "stopped");
+ LOGINFO("Stop service %s: %s.\n", initrc_svc.c_str(), res.c_str());
+ }
+
+ return (res == "stopped");
+}
+
+
+/* Vendor, Firmware and fstab symlink Functions */
+bool is_Vendor_Mounted(void) {
+ static int is_mounted = -1;
+ if (is_mounted < 0)
+ is_mounted = PartitionManager.Is_Mounted_By_Path("/vendor") ? 1 : 0;
+ return is_mounted;
+}
+
+bool is_Firmware_Mounted(void) {
+ static int is_mounted = -1;
+ if (is_mounted < 0)
+ is_mounted = PartitionManager.Is_Mounted_By_Path("/firmware") ? 1 : 0;
+ return is_mounted;
+}
+
+bool will_VendorBin_Be_Symlinked(void) {
+ return (!is_Vendor_Mounted() && TWFunc::Path_Exists("/system/vendor"));
+}
+
+bool Symlink_Vendor_Folder(void) {
+ bool is_vendor_symlinked = false;
+
+ if (is_Vendor_Mounted()) {
+ LOGINFO("vendor partition mounted, skipping /vendor substitution\n");
+ }
+ else if (TWFunc::Path_Exists("/system/vendor")) {
+ LOGINFO("Symlinking vendor folder...\n");
+ if (!TWFunc::Path_Exists("/vendor") || vrename("/vendor", "/vendor-orig") == 0) {
+ TWFunc::Recursive_Mkdir("/vendor/firmware/keymaster");
+ vsymlink("/system/vendor/lib64", "/vendor/lib64");
+ vsymlink("/system/vendor/lib", "/vendor/lib");
+ vsymlink("/system/vendor/bin", "/vendor/bin");
+ is_vendor_symlinked = true;
+ property_set("vold_decrypt.symlinked_vendor", "1");
+ }
+ }
+ return is_vendor_symlinked;
+}
+
+void Restore_Vendor_Folder(void) {
+ property_set("vold_decrypt.symlinked_vendor", "0");
+ TWFunc::removeDir("/vendor", false);
+ vrename("/vendor-orig", "/vendor");
+}
+
+bool Symlink_Firmware_Folder(void) {
+ bool is_firmware_symlinked = false;
+
+ if (is_Firmware_Mounted()) {
+ LOGINFO("firmware partition mounted, skipping /firmware substitution\n");
+ }
+ else {
+ LOGINFO("Symlinking firmware folder...\n");
+ if (!TWFunc::Path_Exists("/firmware") || vrename("/firmware", "/firmware-orig") == 0) {
+ TWFunc::Recursive_Mkdir("/firmware/image");
+ is_firmware_symlinked = true;
+ property_set("vold_decrypt.symlinked_firmware", "1");
+ }
+ }
+ return is_firmware_symlinked;
+}
+
+void Restore_Firmware_Folder(void) {
+ property_set("vold_decrypt.symlinked_firmware", "0");
+ TWFunc::removeDir("/firmware", false);
+ vrename("/firmware-orig", "/firmware");
+}
+
+int Find_Firmware_Files(const string& Path, vector<string> *FileList) {
+ int ret;
+ DIR* d;
+ struct dirent* de;
+ string FileName;
+
+ d = opendir(Path.c_str());
+ if (d == NULL) {
+ closedir(d);
+ return -1;
+ }
+ while ((de = readdir(d)) != NULL) {
+ if (de->d_type == DT_DIR) {
+ if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
+ continue;
+ FileName = Path + "/" + de->d_name;
+ ret = Find_Firmware_Files(FileName, FileList);
+ if (ret < 0)
+ return -1;
+ } else if (de->d_type == DT_REG) {
+ if (fnmatch("keymaste*.*", de->d_name, 0) == 0 || fnmatch("cmnlib.*", de->d_name, 0) == 0) {
+ FileName = Path + "/" + de->d_name;
+ FileList->push_back(FileName);
+ }
+ }
+ }
+ closedir(d);
+ return 0;
+}
+
+void Symlink_Firmware_Files(bool is_vendor_symlinked, bool is_firmware_symlinked) {
+ if (!is_vendor_symlinked && !is_firmware_symlinked)
+ return;
+
+ LOGINFO("Symlinking firmware files...\n");
+
+ vector<string> FirmwareFiles;
+ Find_Firmware_Files("/system", &FirmwareFiles);
+
+ for (size_t i = 0; i < FirmwareFiles.size(); ++i) {
+ string base_name = TWFunc::Get_Filename(FirmwareFiles[i]);
+
+ if (is_firmware_symlinked)
+ vsymlink(FirmwareFiles[i], "/firmware/image/" + base_name);
+
+ if (is_vendor_symlinked) {
+ vsymlink(FirmwareFiles[i], "/vendor/firmware/" + base_name);
+ vsymlink(FirmwareFiles[i], "/vendor/firmware/keymaster/" + base_name);
+ }
+ }
+ LOGINFO("%d file(s) symlinked.\n", (int)FirmwareFiles.size());
+}
+
+// Android 8.0 fs_mgr checks for "/sbin/recovery", in which case it will
+// use /etc/recovery.fstab -> symlink it temporarily. Reference:
+// https://android.googlesource.com/platform/system/core/+/android-8.0.0_r17/fs_mgr/fs_mgr_fstab.cpp#693
+bool Symlink_Recovery_Fstab(void) {
+ bool is_fstab_symlinked = false;
+
+ if (vrename("/etc/recovery.fstab", "/etc/recovery-fstab-orig") == 0) {
+ is_fstab_symlinked = true;
+
+ // now attempt to symlink to /fstab.{ro.hardware}, but even if that
+ // fails, keep TWRP's fstab hidden since it cannot be parsed by fs_mgr
+ char prop_value[PROPERTY_VALUE_MAX];
+ property_get("ro.hardware", prop_value, "error");
+ if (strcmp(prop_value, "error")) {
+ string fstab_device = "/fstab."; fstab_device += prop_value;
+ vsymlink(fstab_device, "/etc/recovery.fstab");
+ }
+ }
+ return is_fstab_symlinked;
+}
+
+void Restore_Recovery_Fstab(void) {
+ unlink("/etc/recovery.fstab");
+ vrename("/etc/recovery-fstab-orig", "/etc/recovery.fstab");
+}
+
+
+/* Additional Services Functions */
+#ifdef TW_CRYPTO_SYSTEM_VOLD_SERVICES
+typedef struct {
+ string Service_Name;
+ string Service_Path;
+ string Service_Binary;
+
+ string VOLD_Service_Name;
+ string TWRP_Service_Name;
+ bool is_running;
+ bool resume;
+ bool exists;
+} AdditionalService;
+
+typedef struct {
+ string Service_Name;
+ string Service_Path;
+ string Service_Binary;
+} RC_Service;
+
+// expand_props() courtesy of platform_system_core_init_util.cpp
+bool expand_props(const std::string& src, std::string* dst) {
+ const char* src_ptr = src.c_str();
+
+ if (!dst) {
+ return false;
+ }
+
+ /* - variables can either be $x.y or ${x.y}, in case they are only part
+ * of the string.
+ * - will accept $$ as a literal $.
+ * - no nested property expansion, i.e. ${foo.${bar}} is not supported,
+ * bad things will happen
+ * - ${x.y:-default} will return default value if property empty.
+ */
+ while (*src_ptr) {
+ const char* c;
+
+ c = strchr(src_ptr, '$');
+ if (!c) {
+ dst->append(src_ptr);
+ return true;
+ }
+
+ dst->append(src_ptr, c);
+ c++;
+
+ if (*c == '$') {
+ dst->push_back(*(c++));
+ src_ptr = c;
+ continue;
+ } else if (*c == '\0') {
+ return true;
+ }
+
+ std::string prop_name;
+ std::string def_val;
+ if (*c == '{') {
+ c++;
+ const char* end = strchr(c, '}');
+ if (!end) {
+ // failed to find closing brace, abort.
+ return false;
+ }
+ prop_name = std::string(c, end);
+ c = end + 1;
+ size_t def = prop_name.find(":-");
+ if (def < prop_name.size()) {
+ def_val = prop_name.substr(def + 2);
+ prop_name = prop_name.substr(0, def);
+ }
+ } else {
+ prop_name = c;
+ c += prop_name.size();
+ }
+
+ if (prop_name.empty()) {
+ return false;
+ }
+
+ char prop_value[PROPERTY_VALUE_MAX];
+ property_get(prop_name.c_str(), prop_value, "");
+ std::string prop_val = prop_value;
+ if (prop_val.empty()) {
+ if (def_val.empty()) {
+ return false;
+ }
+ prop_val = def_val;
+ }
+
+ dst->append(prop_val);
+ src_ptr = c;
+ }
+
+ return true;
+}
+
+string GetArgument(const string& line, size_t argument_number, bool expand_properties) {
+ size_t beg;
+ size_t end;
+ string argument;
+
+ beg = line.find_first_not_of(" \t\r");
+ if (beg == string::npos)
+ return "";
+
+ for (size_t i = 0; i < argument_number; ++i) {
+ end = line.find_first_of(" \t\r", beg);
+ if (end == string::npos)
+ return "";
+
+ beg = line.find_first_not_of(" \t\r", end);
+ if (beg == string::npos)
+ return "";
+ }
+
+ end = line.find_first_of(" \t\r", beg);
+ if (end == string::npos)
+ argument = line.substr(beg);
+ else
+ argument = line.substr(beg, end - beg); // exclude trailing whitespace
+
+ if (expand_properties) {
+ string expanded_property_argument;
+ if (expand_props(argument, &expanded_property_argument))
+ return expanded_property_argument;
+ else
+ return "";
+ } else {
+ return argument;
+ }
+}
+
+// Very simplified .rc parser to get services
+void Parse_RC_File(const string& rc_file, vector<RC_Service>& RC_Services) {
+ ifstream file;
+
+ file.open(rc_file.c_str(), ios::in);
+ if (!file.is_open())
+ return;
+
+ size_t beg; // left trim
+ size_t end; // right trim
+ bool continuation = false; // backslash continuation
+ string line; // line
+ string real_line; // trimmed line with backslash continuation removal
+ vector<string> imports; // file names of imports (we don't want to recursively do while the file is open)
+
+ while (getline(file, line)) {
+ beg = line.find_first_not_of(" \t\r");
+ end = line.find_last_not_of(" \t\r");
+ if (end == string::npos)
+ end = line.length();
+
+ if (beg == string::npos) {
+ if (continuation)
+ continuation = false;
+ else
+ continue;
+ } else if (line[end] == '\\') {
+ continuation = true;
+ real_line += line.substr(beg, end - beg); // excluding backslash
+ continue;
+ } else if (continuation) {
+ continuation = false;
+ real_line += line.substr(beg, end - beg + 1);
+ } else {
+ real_line = line.substr(beg, end - beg + 1);
+ }
+
+ if (GetArgument(real_line, 0, false) == "import") {
+ // handle: import <file>
+ string file_name = GetArgument(real_line, 1, true);
+ if (file_name.empty()) {
+ // INVALID IMPORT
+ } else
+ imports.push_back(file_name);
+ } else if (GetArgument(real_line, 0, false) == "service") {
+ // handle: service <name> <path>
+ RC_Service svc;
+
+ svc.Service_Name = GetArgument(real_line, 1, false);
+ svc.Service_Path = GetArgument(real_line, 2, true);
+
+ if (svc.Service_Name.empty() || svc.Service_Path.empty()) {
+ // INVALID SERVICE ENTRY
+ } else {
+ beg = svc.Service_Path.find_last_of("/");
+ if (beg == string::npos)
+ svc.Service_Binary = svc.Service_Path;
+ else
+ svc.Service_Binary = svc.Service_Path.substr(beg + 1);
+
+/*
+#ifdef _USING_SHORT_SERVICE_NAMES
+ if (svc.Service_Name.length() > 16) {
+ LOGERROR("WARNING: Ignoring service %s (-> %s)\n"
+ " defined in %s is greater than 16 characters and will\n"
+ " not be able to be run by init on android-7.1 or below!\n",
+ svc.Service_Name.c_str(), svc.Service_Path.c_str(), rc_file.c_str()
+ );
+ }
+ else
+#endif
+*/
+ RC_Services.push_back(svc);
+ }
+ }
+ real_line.clear();
+ }
+ file.close();
+
+ for (size_t i = 0; i < imports.size(); ++i) {
+ Parse_RC_File(imports[i], RC_Services);
+ }
+}
+
+vector<AdditionalService> Get_List_Of_Additional_Services(void) {
+ vector<AdditionalService> services;
+
+ // Additional Services needed by vold_decrypt (eg qseecomd, hwservicemanager, etc)
+ vector<string> service_names = TWFunc::Split_String(TW_CRYPTO_SYSTEM_VOLD_SERVICES, " ");
+ for (size_t i = 0; i < service_names.size(); ++i) {
+ AdditionalService svc;
+ svc.Service_Name = service_names[i];
+ svc.exists = false;
+ services.push_back(svc);
+
+#ifdef _USING_SHORT_SERVICE_NAMES
+ // Fallback code for >16 character service names which
+ // allows for multiple definitions in custom .rc files
+ if (service_names[i].length() > 12) {
+ svc.Service_Name = service_names[i].substr(0, 12); // 16-4(prefix)=12
+ svc.exists = false;
+ services.push_back(svc);
+ }
+#endif
+ }
+
+ // Read list of all services defined in all .rc files
+ vector<RC_Service> RC_Services;
+ Parse_RC_File("/init.rc", RC_Services);
+
+
+ // Cross reference Additional Services against the .rc Services and establish
+ // availability of the binaries, otherwise disable it to avoid unnecessary
+ // delays and log spam.
+ // Also check for duplicate entries between TWRP and vold_decrypt so we can
+ // stop and restart any conflicting services.
+ for (size_t i = 0; i < RC_Services.size(); ++i) {
+ string prefix = RC_Services[i].Service_Name.substr(0, 4);
+
+#ifdef _USING_SHORT_SERVICE_NAMES
+ map<string,size_t> rc_indeces;
+#endif
+
+ if (prefix != "sys_" && prefix != "ven_") {
+#ifdef _USING_SHORT_SERVICE_NAMES
+ if (RC_Services[i].Service_Name.length() > 12) {
+ // save this entry for potential binary name match
+ rc_indeces.insert(pair<string,size_t>(RC_Services[i].Service_Binary, i));
+ }
+#endif
+ continue;
+ }
+
+ for (size_t j = 0; j < services.size(); ++j) {
+ string path = RC_Services[i].Service_Path;
+ if (prefix == "ven_" && will_VendorBin_Be_Symlinked()) {
+ path = "/system" + path; // vendor is going to get symlinked to /system/vendor
+ }
+
+ if (RC_Services[i].Service_Name == prefix + services[j].Service_Name) {
+ if (!services[j].VOLD_Service_Name.empty() && TWFunc::Path_Exists(path)) {
+ // Duplicate match, log but use previous definition
+ LOGERROR("Service %s: VOLD_Service_Name already defined as %s\n", RC_Services[i].Service_Name.c_str(), services[j].VOLD_Service_Name.c_str());
+ }
+ else if (TWFunc::Path_Exists(path)) {
+ services[j].exists = true;
+ services[j].VOLD_Service_Name = RC_Services[i].Service_Name; // prefix + service_name
+ services[j].Service_Path = RC_Services[i].Service_Path;
+ services[j].Service_Binary = RC_Services[i].Service_Binary;
+
+ if (Service_Exists(services[j].Service_Name))
+ services[j].TWRP_Service_Name = services[j].Service_Name;
+ else if (Service_Exists("sbin" + services[j].Service_Name))
+ services[j].TWRP_Service_Name = "sbin" + services[j].Service_Name;
+ else
+ services[j].TWRP_Service_Name.clear();
+
+#ifdef _USING_SHORT_SERVICE_NAMES
+ if (services[j].TWRP_Service_Name.empty()) {
+ // Try matching Service_Binary (due to 16 character service_name limit in 7.1 and below)
+ map<string,size_t>::iterator it = rc_indeces.find(services[j].Service_Binary);
+ if (it != rc_indeces.end()) {
+ services[j].TWRP_Service_Name = RC_Services[it->second].Service_Name;
+ }
+ }
+#endif
+ }
+ break;
+ }
+ }
+ }
+
+ LOGINFO("List of additional services for vold_decrypt:\n");
+ for (size_t i = 0; i < services.size(); ++i) {
+ if (services[i].exists) {
+ if (services[i].TWRP_Service_Name.empty()) {
+ LOGINFO(" %s: Enabled as %s -> %s\n",
+ services[i].Service_Name.c_str(),
+ services[i].VOLD_Service_Name.c_str(), services[i].Service_Path.c_str()
+ );
+ } else {
+ LOGINFO(" %s: Enabled as %s -> %s (temporarily replacing TWRP's %s service)\n",
+ services[i].Service_Name.c_str(),
+ services[i].VOLD_Service_Name.c_str(), services[i].Service_Path.c_str(),
+ services[i].TWRP_Service_Name.c_str()
+ );
+ }
+ }
+ else
+ LOGINFO(" %s: Disabled due to lack of matching binary\n", services[i].Service_Name.c_str());
+ }
+ return services;
+}
+#endif
+
+
+/* Misc Functions */
+void Set_Needed_Properties(void) {
+ // vold won't start without ro.storage_structure on Kitkat
+ string sdkverstr = TWFunc::System_Property_Get("ro.build.version.sdk");
+ int sdkver = 20;
+ if (!sdkverstr.empty()) {
+ sdkver = atoi(sdkverstr.c_str());
+ }
+ if (sdkver <= 19) {
+ string ro_storage_structure = TWFunc::System_Property_Get("ro.storage_structure");
+ if (!ro_storage_structure.empty())
+ property_set("ro.storage_structure", ro_storage_structure.c_str());
+ }
+}
+
+
+/* vdc Functions */
+typedef struct {
+ string Output; // Entire line excluding \n
+ int ResponseCode; // ResponseCode.h (int)
+ int Sequence; // Sequence (int)
+ int Message; // Message (string) but we're only interested in int
+} vdc_ReturnValues;
+
+int Exec_vdc_cryptfs(const string& command, const string& argument, vdc_ReturnValues* vdcResult) {
+ pid_t pid;
+ int status;
+ int pipe_fd[2];
+ vdcResult->Output.clear();
+
+ if (pipe(pipe_fd)) {
+ LOGERROR("exec_vdc_cryptfs: pipe() error!\n");
+ return -1;
+ }
+
+ const char *cmd[] = { "/system/bin/vdc", "cryptfs" };
+ const char *env[] = { "LD_LIBRARY_PATH=/system/lib64:/system/lib", NULL };
+
+#ifdef TW_CRYPTO_SYSTEM_VOLD_DEBUG
+ string log_name = "/tmp/strace_vdc_" + command;
+#endif
+
+ switch(pid = fork())
+ {
+ case -1:
+ LOGERROR("exec_vdc_cryptfs: fork failed: %d (%s)!\n", errno, strerror(errno));
+ return -1;
+
+ case 0: // child
+ fflush(stdout); fflush(stderr);
+ close(pipe_fd[0]);
+ dup2(pipe_fd[1], STDOUT_FILENO);
+ dup2(pipe_fd[1], STDERR_FILENO);
+ close(pipe_fd[1]);
+
+#ifdef TW_CRYPTO_SYSTEM_VOLD_DEBUG
+ if (has_strace) {
+ if (argument.empty())
+ execl(VD_STRACE_BIN, "strace", "-q", "-tt", "-ff", "-v", "-y", "-s", "1000", "-o", log_name.c_str(),
+ "-E", env[0], cmd[0], cmd[1], command.c_str(), NULL);
+ else
+ execl(VD_STRACE_BIN, "strace", "-q", "-tt", "-ff", "-v", "-y", "-s", "1000", "-o", log_name.c_str(),
+ "-E", env[0], cmd[0], cmd[1], command.c_str(), argument.c_str(), NULL);
+ } else
+#endif
+ if (argument.empty())
+ execle(cmd[0], cmd[0], cmd[1], command.c_str(), NULL, env);
+ else
+ execle(cmd[0], cmd[0], cmd[1], command.c_str(), argument.c_str(), NULL, env);
+ _exit(127);
+ break;
+
+ default:
+ {
+ int timeout = 30*100;
+ vdcResult->Output.clear();
+ close(pipe_fd[1]);
+
+ // Non-blocking read loop with timeout
+ int flags = fcntl(pipe_fd[0], F_GETFL, 0);
+ fcntl(pipe_fd[0], F_SETFL, flags | O_NONBLOCK);
+
+ char buffer[128];
+ ssize_t count;
+ pid_t retpid = waitpid(pid, &status, WNOHANG);
+ while (true) {
+ count = read(pipe_fd[0], buffer, sizeof(buffer));
+ if (count == -1) {
+ if (errno == EINTR)
+ continue;
+ else if (errno != EAGAIN)
+ LOGERROR("exec_vdc_cryptfs: read() error %d (%s)\n!", errno, strerror(errno));
+ } else if (count > 0) {
+ vdcResult->Output.append(buffer, count);
+ }
+
+ retpid = waitpid(pid, &status, WNOHANG);
+ if (retpid == 0 && --timeout)
+ usleep(10000);
+ else
+ break;
+ };
+ close(pipe_fd[0]);
+ if (vdcResult->Output.length() > 0 && vdcResult->Output[vdcResult->Output.length() - 1] == '\n')
+ vdcResult->Output.erase(vdcResult->Output.length() - 1);
+ vdcResult->ResponseCode = vdcResult->Sequence = vdcResult->Message = -1;
+ sscanf(vdcResult->Output.c_str(), "%d %d %d", &vdcResult->ResponseCode, &vdcResult->Sequence, &vdcResult->Message);
+
+ // Error handling
+ if (retpid == 0 && timeout == 0) {
+ LOGERROR("exec_vdc_cryptfs: took too long, killing process\n");
+ kill(pid, SIGKILL);
+ for (timeout = 5; retpid == 0 && timeout; --timeout) {
+ sleep(1);
+ retpid = waitpid(pid, &status, WNOHANG);
+ }
+ if (retpid)
+ LOGINFO("exec_vdc_cryptfs: process killed successfully\n");
+ else
+ LOGERROR("exec_vdc_cryptfs: process took too long to kill, may be a zombie process\n");
+ return VD_ERR_VOLD_OPERATION_TIMEDOUT;
+ } else if (retpid > 0) {
+ if (WIFSIGNALED(status)) {
+ LOGERROR("exec_vdc_cryptfs: process ended with signal: %d\n", WTERMSIG(status)); // Seg fault or some other non-graceful termination
+ return -1;
+ }
+ } else if (retpid < 0) { // no PID returned
+ if (errno == ECHILD)
+ LOGINFO("exec_vdc_cryptfs: no child process exist\n");
+ else {
+ LOGERROR("exec_vdc_cryptfs: Unexpected error %d (%s)\n", errno, strerror(errno));
+ return -1;
+ }
+ }
+ return 0;
+ }
+ }
+}
+
+int Run_vdc(const string& Password) {
+ int res;
+ struct timeval t1, t2;
+ vdc_ReturnValues vdcResult;
+
+ LOGINFO("About to run vdc...\n");
+
+ // Wait for vold connection
+ gettimeofday(&t1, NULL);
+ t2 = t1;
+ while ((t2.tv_sec - t1.tv_sec) < 5) {
+ // cryptfs getpwtype returns: R1=213(PasswordTypeResult) R2=? R3="password", "pattern", "pin", "default"
+ res = Exec_vdc_cryptfs("getpwtype", "", &vdcResult);
+ if (vdcResult.ResponseCode == PASSWORD_TYPE_RESULT) {
+ char str_res[4 + sizeof(int) + 1];
+ snprintf(str_res, sizeof(str_res), "ret=%d", res);
+ vdcResult.Output += str_res;
+ res = 0;
+ break;
+ }
+ LOGINFO("Retrying connection to vold (Reason: %s)\n", vdcResult.Output.c_str());
+ usleep(SLEEP_MIN_USEC); // vdc usually usleep(10000), but that causes too many unnecessary attempts
+ gettimeofday(&t2, NULL);
+ }
+
+ if (res == 0 && (t2.tv_sec - t1.tv_sec) < 5)
+ LOGINFO("Connected to vold (%s)\n", vdcResult.Output.c_str());
+ else if (res == VD_ERR_VOLD_OPERATION_TIMEDOUT)
+ return VD_ERR_VOLD_OPERATION_TIMEDOUT; // should never happen for getpwtype
+ else if (res)
+ return VD_ERR_FORK_EXECL_ERROR;
+ else if (vdcResult.ResponseCode != -1)
+ return VD_ERR_VOLD_UNEXPECTED_RESPONSE;
+ else
+ return VD_ERR_VDC_FAILED_TO_CONNECT;
+
+
+ // Input password from GUI, or default password
+ res = Exec_vdc_cryptfs("checkpw", Password, &vdcResult);
+ if (res == VD_ERR_VOLD_OPERATION_TIMEDOUT)
+ return VD_ERR_VOLD_OPERATION_TIMEDOUT;
+ else if (res)
+ return VD_ERR_FORK_EXECL_ERROR;
+
+ LOGINFO("vdc cryptfs result (passwd): %s (ret=%d)\n", vdcResult.Output.c_str(), res);
+ /*
+ if (res == 0 && vdcResult.ResponseCode != COMMAND_OKAY)
+ return VD_ERR_VOLD_UNEXPECTED_RESPONSE;
+ */
+
+ if (vdcResult.Message != 0) {
+ // try falling back to Lollipop hex passwords
+ string hexPassword = convert_key_to_hex_ascii(Password);
+ res = Exec_vdc_cryptfs("checkpw", hexPassword, &vdcResult);
+ if (res == VD_ERR_VOLD_OPERATION_TIMEDOUT)
+ return VD_ERR_VOLD_OPERATION_TIMEDOUT;
+ else if (res)
+ return VD_ERR_FORK_EXECL_ERROR;
+
+ LOGINFO("vdc cryptfs result (hex_pw): %s (ret=%d)\n", vdcResult.Output.c_str(), res);
+ /*
+ if (res == 0 && vdcResult.ResponseCode != COMMAND_OKAY)
+ return VD_ERR_VOLD_UNEXPECTED_RESPONSE;
+ */
+ }
+
+ // vdc's return value is dependant upon source origin, it will either
+ // return 0 or ResponseCode, so disregard and focus on decryption instead
+ if (vdcResult.Message == 0) {
+ // Decryption successful wait for crypto blk dev
+ Wait_For_Property("ro.crypto.fs_crypto_blkdev");
+ res = VD_SUCCESS;
+ } else if (vdcResult.ResponseCode != COMMAND_OKAY) {
+ res = VD_ERR_VOLD_UNEXPECTED_RESPONSE;
+ } else {
+ res = VD_ERR_DECRYPTION_FAILED;
+ }
+
+ return res;
+}
+
+int Vold_Decrypt_Core(const string& Password) {
+ int res;
+ bool is_vendor_symlinked = false;
+ bool is_firmware_symlinked = false;
+ bool is_fstab_symlinked = false;
+ bool is_vold_running = false;
+
+ if (Password.empty()) {
+ LOGINFO("vold_decrypt: password is empty!\n");
+ return VD_ERR_PASSWORD_EMPTY;
+ }
+
+ // Mount system and check for vold and vdc
+ if (!PartitionManager.Mount_By_Path("/system", true)) {
+ return VD_ERR_UNABLE_TO_MOUNT_SYSTEM;
+ } else if (!TWFunc::Path_Exists("/system/bin/vold")) {
+ LOGINFO("ERROR: /system/bin/vold not found, aborting.\n");
+ return VD_ERR_MISSING_VOLD;
+ } else if (!TWFunc::Path_Exists("/system/bin/vdc")) {
+ LOGINFO("ERROR: /system/bin/vdc not found, aborting.\n");
+ return VD_ERR_MISSING_VDC;
+ }
+
+ fp_kmsg = fopen("/dev/kmsg", "a");
+
+ LOGINFO("TW_CRYPTO_USE_SYSTEM_VOLD := true\n");
+
+ // just cache the result to avoid unneeded duplicates in recovery.log
+ LOGINFO("Checking existence of vendor and firmware partitions...\n");
+ is_Vendor_Mounted();
+ is_Firmware_Mounted();
+
+ LOGINFO("Attempting to use system's vold for decryption...\n");
+
+#ifdef TW_CRYPTO_SYSTEM_VOLD_DEBUG
+ Strace_init_Start();
+#endif
+
+#ifdef TW_CRYPTO_SYSTEM_VOLD_SERVICES
+ vector<AdditionalService> Services = Get_List_Of_Additional_Services();
+
+ // Check if TWRP is running any of the services
+ for (size_t i = 0; i < Services.size(); ++i) {
+ if (!Services[i].TWRP_Service_Name.empty() && !Is_Service_Stopped(Services[i].TWRP_Service_Name)) {
+ Services[i].resume = true;
+ Stop_Service(Services[i].TWRP_Service_Name);
+ } else
+ Services[i].resume = false;
+ }
+#endif
+
+ LOGINFO("Setting up folders and permissions...\n");
+ is_fstab_symlinked = Symlink_Recovery_Fstab();
+ is_vendor_symlinked = Symlink_Vendor_Folder();
+ is_firmware_symlinked = Symlink_Firmware_Folder();
+ Symlink_Firmware_Files(is_vendor_symlinked, is_firmware_symlinked);
+
+ Set_Needed_Properties();
+
+ // Start services needed for vold decrypt
+ LOGINFO("Starting services...\n");
+#ifdef TW_CRYPTO_SYSTEM_VOLD_SERVICES
+ for (size_t i = 0; i < Services.size(); ++i) {
+ if (Services[i].exists)
+ Services[i].is_running = Start_Service(Services[i].VOLD_Service_Name);
+ }
+#endif
+ is_vold_running = Start_Service("sys_vold");
+
+ if (is_vold_running) {
+#ifdef TW_CRYPTO_SYSTEM_VOLD_SERVICES
+ for (size_t i = 0; i < Services.size(); ++i) {
+ if (Services[i].exists && !Is_Service_Running(Services[i].VOLD_Service_Name) && Services[i].resume) {
+ // if system_service has died restart the twrp_service
+ LOGINFO("%s is not running, resuming %s!\n", Services[i].VOLD_Service_Name.c_str(), Services[i].TWRP_Service_Name.c_str());
+ Start_Service(Services[i].TWRP_Service_Name);
+ }
+ }
+#endif
+ res = Run_vdc(Password);
+
+ if (res != 0) {
+ LOGINFO("Decryption failed\n");
+ }
+ } else {
+ LOGINFO("Failed to start vold\n");
+ res = VD_ERR_VOLD_FAILED_TO_START;
+ }
+
+ // Stop services needed for vold decrypt so /system can be unmounted
+ LOGINFO("Stopping services...\n");
+ Stop_Service("sys_vold");
+#ifdef TW_CRYPTO_SYSTEM_VOLD_SERVICES
+ for (size_t i = 0; i < Services.size(); ++i) {
+ if (!Is_Service_Running(Services[i].VOLD_Service_Name) && Services[i].resume)
+ Stop_Service(Services[i].TWRP_Service_Name);
+ else if (Services[i].exists)
+ Stop_Service(Services[i].VOLD_Service_Name);
+ }
+#endif
+
+ if (is_firmware_symlinked)
+ Restore_Firmware_Folder();
+ if (is_vendor_symlinked)
+ Restore_Vendor_Folder();
+ if (is_fstab_symlinked)
+ Restore_Recovery_Fstab();
+
+ if (!PartitionManager.UnMount_By_Path("/system", true)) {
+ // PartitionManager failed to unmount /system, this should not happen,
+ // but in case it does, do a lazy unmount
+ LOGINFO("WARNING: system could not be unmounted normally!\n");
+ umount2("/system", MNT_DETACH);
+ }
+
+ LOGINFO("Finished.\n");
+
+#ifdef TW_CRYPTO_SYSTEM_VOLD_SERVICES
+ // Restart previously running services
+ for (size_t i = 0; i < Services.size(); ++i) {
+ if (Services[i].resume)
+ Start_Service(Services[i].TWRP_Service_Name);
+ }
+#endif
+
+#ifdef TW_CRYPTO_SYSTEM_VOLD_DEBUG
+ Strace_init_Stop();
+#endif
+
+ // Finish up and exit
+ if (fp_kmsg) {
+ fflush(fp_kmsg);
+ fclose(fp_kmsg);
+ }
+
+ return res;
+}
+
+} // namespace
+
+/*
+ * Common vold Response Codes / Errors:
+ * 406 (OpFailedStorageNotFound) -> Problem reading or parsing fstab
+ *
+ */
+
+/* Main function separated from core in case we want to return error info */
+int vold_decrypt(const string& Password) {
+ return Vold_Decrypt_Core(Password);
+}
diff --git a/crypto/vold_decrypt/vold_decrypt.h b/crypto/vold_decrypt/vold_decrypt.h
new file mode 100644
index 000000000..ba7a74720
--- /dev/null
+++ b/crypto/vold_decrypt/vold_decrypt.h
@@ -0,0 +1,42 @@
+/*
+ Copyright 2017 TeamWin
+ This file is part of TWRP/TeamWin Recovery Project.
+
+ TWRP is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ TWRP is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with TWRP. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _VOLD_DECRYPT_H
+#define _VOLD_DECRYPT_H
+
+#include <string>
+
+// -_-
+enum {
+ VD_SUCCESS = 0,
+ VD_ERR_DECRYPTION_FAILED = -1,
+ VD_ERR_UNABLE_TO_MOUNT_SYSTEM = -2,
+ VD_ERR_MISSING_VOLD = -3,
+ VD_ERR_MISSING_VDC = -4,
+ VD_ERR_VDC_FAILED_TO_CONNECT = -5,
+ VD_ERR_VOLD_FAILED_TO_START = -6,
+ VD_ERR_VOLD_UNEXPECTED_RESPONSE = -7,
+ VD_ERR_VOLD_OPERATION_TIMEDOUT = -8,
+ VD_ERR_FORK_EXECL_ERROR = -9,
+ VD_ERR_PASSWORD_EMPTY = -10,
+};
+
+
+int vold_decrypt(const std::string& Password);
+
+#endif // _VOLD_DECRYPT_H