diff options
Diffstat (limited to 'crypto')
52 files changed, 7952 insertions, 0 deletions
diff --git a/crypto/ext4crypt/Android.mk b/crypto/ext4crypt/Android.mk new file mode 100644 index 000000000..bcbcccfa2 --- /dev/null +++ b/crypto/ext4crypt/Android.mk @@ -0,0 +1,44 @@ +LOCAL_PATH := $(call my-dir) +ifeq ($(TW_INCLUDE_CRYPTO), true) +include $(CLEAR_VARS) + +LOCAL_MODULE := libe4crypt +LOCAL_MODULE_TAGS := eng optional +LOCAL_CFLAGS := +LOCAL_SRC_FILES := Decrypt.cpp Ext4Crypt.cpp Keymaster.cpp KeyStorage.cpp ScryptParameters.cpp Utils.cpp HashPassword.cpp 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 external/scrypt/lib/crypto system/security/keystore hardware/libhardware/include/hardware system/security/softkeymaster/include/keymaster system/keymaster/include + +ifneq ($(wildcard hardware/libhardware/include/hardware/keymaster0.h),) + LOCAL_CFLAGS += -DTW_CRYPTO_HAVE_KEYMASTERX + LOCAL_C_INCLUDES += external/boringssl/src/include +endif + +include $(BUILD_SHARED_LIBRARY) + + + +include $(CLEAR_VARS) +LOCAL_MODULE := twrpfbe +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_CLASS := RECOVERY_EXECUTABLES +LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin +LOCAL_SRC_FILES := main.cpp +LOCAL_SHARED_LIBRARIES := libe4crypt +#LOCAL_LDFLAGS += -Wl,-dynamic-linker,/sbin/linker64 + +include $(BUILD_EXECUTABLE) + +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..3b69d4651 --- /dev/null +++ b/crypto/ext4crypt/Decrypt.cpp @@ -0,0 +1,222 @@ +/* + * 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> + +#include "ext4_crypt.h" +#include "key_control.h" + +#include <hardware/gatekeeper.h> +#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); + 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; +} + +int gatekeeper_device_initialize(gatekeeper_device_t **dev) { + int ret; + const hw_module_t *mod; + ret = hw_get_module_by_class(GATEKEEPER_HARDWARE_MODULE_ID, NULL, &mod); + + if (ret!=0) { + printf("failed to get hw module\n"); + return ret; + } + + ret = gatekeeper_open(mod, dev); + + if (ret!=0) + printf("failed to open gatekeeper\n"); + return ret; +} + +int Get_Password_Type(const userid_t user_id, std::string& filename) { + std::string path; + if (user_id == 0) { + path = "/data/system/"; + } else { + char user_id_str[5]; + sprintf(user_id_str, "%i", user_id); + path = "/data/system/users/"; + path += user_id_str; + path += "/"; + } + filename = path + "gatekeeper.password.key"; + struct stat st; + if (stat(filename.c_str(), &st) == 0 && st.st_size > 0) + return 1; + filename = path + "gatekeeper.pattern.key"; + if (stat(filename.c_str(), &st) == 0 && st.st_size > 0) + return 2; + printf("Unable to locate gatekeeper password file '%s'\n", filename.c_str()); + filename = ""; + return 0; +} + +bool Decrypt_DE() { + if (!e4crypt_initialize_global_de()) { // this deals with the overarching device encryption + printf("e4crypt_initialize_global_de returned fail\n"); + return false; + } + if (!e4crypt_init_user0()) { + printf("e4crypt_init_user0 returned fail\n"); + return false; + } + return true; +} + +bool Decrypt_User(const userid_t user_id, const std::string& Password) { + uint8_t *auth_token; + uint32_t auth_token_len; + int ret; + + struct stat st; + if (user_id > 9999) { + printf("user_id is too big\n"); + return false; + } + std::string filename; + bool Default_Password = (Password == "!"); + if (Get_Password_Type(user_id, filename) == 0 && !Default_Password) { + printf("Unknown password type\n"); + return false; + } + int flags = FLAG_STORAGE_DE; + if (user_id == 0) + flags = FLAG_STORAGE_DE; + else + flags = FLAG_STORAGE_CE; + gatekeeper_device_t *device; + ret = gatekeeper_device_initialize(&device); + if (Default_Password) { + if (!e4crypt_unlock_user_key(user_id, 0, "!", "!")) { + printf("e4crypt_unlock_user_key returned fail\n"); + return false; + } + if (!e4crypt_prepare_user_storage(nullptr, user_id, 0, flags)) { + printf("failed to e4crypt_prepare_user_storage\n"); + return false; + } + printf("Decrypted Successfully!\n"); + return true; + } + if (ret!=0) + return false; + printf("password filename is '%s'\n", filename.c_str()); + if (stat(filename.c_str(), &st) != 0) { + printf("error stat'ing key file: %s\n", strerror(errno)); + return false; + } + std::string handle; + if (!android::base::ReadFileToString(filename, &handle)) { + printf("Failed to read '%s'\n", filename.c_str()); + return false; + } + bool should_reenroll; + ret = device->verify(device, user_id, 0, (const uint8_t *)handle.c_str(), st.st_size, + (const uint8_t *)Password.c_str(), (uint32_t)Password.size(), &auth_token, &auth_token_len, + &should_reenroll); + if (ret !=0) { + printf("failed to verify\n"); + return false; + } + char token_hex[(auth_token_len*2)+1]; + token_hex[(auth_token_len*2)] = 0; + uint32_t i; + for (i=0;i<auth_token_len;i++) { + sprintf(&token_hex[2*i], "%02X", auth_token[i]); + } + // The secret is "Android FBE credential hash" plus appended 0x00 to reach 128 bytes then append the user's password then feed that to sha512sum + std::string secret = HashPassword(Password); + if (!e4crypt_unlock_user_key(user_id, 0, token_hex, secret.c_str())) { + printf("e4crypt_unlock_user_key returned fail\n"); + return false; + } + if (!e4crypt_prepare_user_storage(nullptr, user_id, 0, flags)) { + printf("failed to e4crypt_prepare_user_storage\n"); + return false; + } + printf("Decrypted Successfully!\n"); + return true; +} 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..8bc419992 --- /dev/null +++ b/crypto/ext4crypt/Ext4Crypt.cpp @@ -0,0 +1,525 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Ext4Crypt.h" +#include "Decrypt.h" + +#include "KeyStorage.h" +#include "Utils.h" + +#include <algorithm> +#include <iomanip> +#include <map> +#include <set> +#include <sstream> +#include <string> + +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <openssl/sha.h> +#include <selinux/android.h> +#include <stdio.h> +#include <sys/mount.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <iostream> + +#include <private/android_filesystem_config.h> + +#include "ext4_crypt.h" +#include "key_control.h" + +#include <hardware/gatekeeper.h> +#include "HashPassword.h" + +#define EMULATED_USES_SELINUX 0 +#define MANAGE_MISC_DIRS 0 + +#include <cutils/fs.h> + +#include <android-base/file.h> +//#include <android-base/logging.h> +#include <android-base/stringprintf.h> + +#define LOG(x) std::cout +#define PLOG(x) std::cout +#define DATA_MNT_POINT "/data" + +using android::base::StringPrintf; +using android::vold::kEmptyAuthentication; + +// NOTE: keep in sync with StorageManager +//static constexpr int FLAG_STORAGE_DE = 1 << 0; // moved to Decrypt.h +//static constexpr int FLAG_STORAGE_CE = 1 << 1; + +// 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..86e067ebb --- /dev/null +++ b/crypto/ext4crypt/HashPassword.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2016 Team Win Recovery Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * This computes the "secret" used by Android as one of the parameters + * to decrypt File Based Encryption. The secret is prefixed with + * "Android FBE credential hash" padded with 0s to 128 bytes then the + * user's password is appended to the end of the 128 bytes. This string + * is then hashed with sha512 and the sha512 value is then converted to + * hex with upper-case characters. + */ + +#include <stdio.h> +#include <string> +#include <stdlib.h> +#include <openssl/sha.h> + +#define PASS_PADDING_SIZE 128 +#define SHA512_HEX_SIZE SHA512_DIGEST_LENGTH * 2 + +std::string HashPassword(const std::string& Password) { + size_t size = PASS_PADDING_SIZE + Password.size(); + unsigned char* buffer = (unsigned char*)calloc(1, size); + const char* prefix = "Android FBE credential hash"; + memcpy((void*)buffer, (void*)prefix, strlen(prefix)); + unsigned char* ptr = buffer + PASS_PADDING_SIZE; + memcpy((void*)ptr, Password.c_str(), Password.size()); + unsigned char hash[SHA512_DIGEST_LENGTH]; + SHA512_CTX sha512; + SHA512_Init(&sha512); + SHA512_Update(&sha512, buffer, size); + SHA512_Final(hash, &sha512); + int index = 0; + char hex_hash[SHA512_HEX_SIZE + 1]; + for(index = 0; index < SHA512_DIGEST_LENGTH; index++) + sprintf(hex_hash + (index * 2), "%02X", hash[index]); + hex_hash[128] = 0; + std::string ret = hex_hash; + return ret; +} diff --git a/crypto/ext4crypt/HashPassword.h b/crypto/ext4crypt/HashPassword.h new file mode 100644 index 000000000..d9b5ce5f1 --- /dev/null +++ b/crypto/ext4crypt/HashPassword.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2016 Team Win Recovery Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __HASH_PASSWORD_H +#define __HASH_PASSWORD_H + +#include <string> + +std::string HashPassword(const std::string& Password); + +#endif 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(¶mBuilder, keymaster::TAG_APPLICATION_ID, appId); + if (auth.token.empty()) { + LOG(DEBUG) << "Creating key that doesn't need auth token"; + paramBuilder.Authorization(keymaster::TAG_NO_AUTH_REQUIRED); + } else { + LOG(DEBUG) << "Auth token required for key"; + if (auth.token.size() != sizeof(hw_auth_token_t)) { + LOG(ERROR) << "Auth token should be " << sizeof(hw_auth_token_t) << " bytes, was " + << auth.token.size() << " bytes"; + return false; + } + const hw_auth_token_t* at = reinterpret_cast<const hw_auth_token_t*>(auth.token.data()); + paramBuilder.Authorization(keymaster::TAG_USER_SECURE_ID, at->user_id); + paramBuilder.Authorization(keymaster::TAG_USER_AUTH_TYPE, HW_AUTH_PASSWORD); + paramBuilder.Authorization(keymaster::TAG_AUTH_TIMEOUT, AUTH_TIMEOUT); + } + return keymaster.generateKey(paramBuilder.build(), key); +}*/ + +static keymaster::AuthorizationSetBuilder beginParams(const KeyAuthentication& auth, + const std::string& appId) { + auto paramBuilder = keymaster::AuthorizationSetBuilder() + .Authorization(keymaster::TAG_BLOCK_MODE, KM_MODE_GCM) + .Authorization(keymaster::TAG_MAC_LENGTH, GCM_MAC_BYTES * 8) + .Authorization(keymaster::TAG_PADDING, KM_PAD_NONE); + addStringParam(¶mBuilder, keymaster::TAG_APPLICATION_ID, appId); + if (!auth.token.empty()) { + LOG(DEBUG) << "Supplying auth token to Keymaster"; + addStringParam(¶mBuilder, keymaster::TAG_AUTH_TOKEN, auth.token); + } + return paramBuilder; +} + +/*static bool encryptWithKeymasterKey(Keymaster& keymaster, const std::string& key, + const KeyAuthentication& auth, const std::string& appId, + const std::string& message, std::string* ciphertext) { + auto params = beginParams(auth, appId).build(); + keymaster::AuthorizationSet outParams; + auto opHandle = keymaster.begin(KM_PURPOSE_ENCRYPT, key, params, &outParams); + if (!opHandle) return false; + keymaster_blob_t nonceBlob; + if (!outParams.GetTagValue(keymaster::TAG_NONCE, &nonceBlob)) { + LOG(ERROR) << "GCM encryption but no nonce generated"; + return false; + } + // nonceBlob here is just a pointer into existing data, must not be freed + std::string nonce(reinterpret_cast<const char*>(nonceBlob.data), nonceBlob.data_length); + if (!checkSize("nonce", nonce.size(), GCM_NONCE_BYTES)) return false; + std::string body; + if (!opHandle.updateCompletely(message, &body)) return false; + + std::string mac; + if (!opHandle.finishWithOutput(&mac)) return false; + if (!checkSize("mac", mac.size(), GCM_MAC_BYTES)) return false; + *ciphertext = nonce + body + mac; + return true; +}*/ + +static bool decryptWithKeymasterKey(Keymaster& keymaster, const std::string& key, + const KeyAuthentication& auth, const std::string& appId, + const std::string& ciphertext, std::string* message) { + auto nonce = ciphertext.substr(0, GCM_NONCE_BYTES); + auto bodyAndMac = ciphertext.substr(GCM_NONCE_BYTES); + auto params = addStringParam(beginParams(auth, appId), keymaster::TAG_NONCE, nonce).build(); + auto opHandle = keymaster.begin(KM_PURPOSE_DECRYPT, key, params); + if (!opHandle) return false; + if (!opHandle.updateCompletely(bodyAndMac, message)) return false; + if (!opHandle.finish()) return false; + return true; +} + +static bool readFileToString(const std::string& filename, std::string* result) { + if (!android::base::ReadFileToString(filename, result)) { + PLOG(ERROR) << "Failed to read from " << filename; + return false; + } + return true; +} + +/*static bool writeStringToFile(const std::string& payload, const std::string& filename) { + if (!android::base::WriteStringToFile(payload, filename)) { + PLOG(ERROR) << "Failed to write to " << filename; + return false; + } + return true; +}*/ + +static std::string getStretching() { + char paramstr[PROPERTY_VALUE_MAX]; + + property_get(SCRYPT_PROP, paramstr, SCRYPT_DEFAULTS); + return std::string() + kStretchPrefix_scrypt + paramstr; +} + +static bool stretchingNeedsSalt(const std::string& stretching) { + return stretching != kStretch_nopassword && stretching != kStretch_none; +} + +static bool stretchSecret(const std::string& stretching, const std::string& secret, + const std::string& salt, std::string* stretched) { + if (stretching == kStretch_nopassword) { + if (!secret.empty()) { + LOG(WARNING) << "Password present but stretching is nopassword"; + // Continue anyway + } + stretched->clear(); + } else if (stretching == kStretch_none) { + *stretched = secret; + } else if (std::equal(kStretchPrefix_scrypt.begin(), kStretchPrefix_scrypt.end(), + stretching.begin())) { + int Nf, rf, pf; + if (!parse_scrypt_parameters(stretching.substr(kStretchPrefix_scrypt.size()).c_str(), &Nf, + &rf, &pf)) { + LOG(ERROR) << "Unable to parse scrypt params in stretching: " << stretching; + return false; + } + stretched->assign(STRETCHED_BYTES, '\0'); + if (crypto_scrypt(reinterpret_cast<const uint8_t*>(secret.data()), secret.size(), + reinterpret_cast<const uint8_t*>(salt.data()), salt.size(), + 1 << Nf, 1 << rf, 1 << pf, + reinterpret_cast<uint8_t*>(&(*stretched)[0]), stretched->size()) != 0) { + LOG(ERROR) << "scrypt failed with params: " << stretching; + return false; + } + } else { + LOG(ERROR) << "Unknown stretching type: " << stretching; + return false; + } + return true; +} + +static bool generateAppId(const KeyAuthentication& auth, const std::string& stretching, + const std::string& salt, const std::string& secdiscardable, + std::string* appId) { + std::string stretched; + if (!stretchSecret(stretching, auth.secret, salt, &stretched)) return false; + *appId = hashSecdiscardable(secdiscardable) + stretched; + return true; +} + +/*bool storeKey(const std::string& dir, const KeyAuthentication& auth, const std::string& key) { + if (TEMP_FAILURE_RETRY(mkdir(dir.c_str(), 0700)) == -1) { + PLOG(ERROR) << "key mkdir " << dir; + return false; + } + if (!writeStringToFile(kCurrentVersion, dir + "/" + kFn_version)) return false; + std::string secdiscardable; + if (ReadRandomBytes(SECDISCARDABLE_BYTES, secdiscardable) != OK) { + // TODO status_t plays badly with PLOG, fix it. + LOG(ERROR) << "Random read failed"; + return false; + } + if (!writeStringToFile(secdiscardable, dir + "/" + kFn_secdiscardable)) return false; + std::string stretching = auth.secret.empty() ? kStretch_nopassword : getStretching(); + if (!writeStringToFile(stretching, dir + "/" + kFn_stretching)) return false; + std::string salt; + if (stretchingNeedsSalt(stretching)) { + if (ReadRandomBytes(SALT_BYTES, salt) != OK) { + LOG(ERROR) << "Random read failed"; + return false; + } + if (!writeStringToFile(salt, dir + "/" + kFn_salt)) return false; + } + std::string appId; + if (!generateAppId(auth, stretching, salt, secdiscardable, &appId)) return false; + Keymaster keymaster; + if (!keymaster) return false; + std::string kmKey; + if (!generateKeymasterKey(keymaster, auth, appId, &kmKey)) return false; + if (!writeStringToFile(kmKey, dir + "/" + kFn_keymaster_key_blob)) return false; + std::string encryptedKey; + if (!encryptWithKeymasterKey(keymaster, kmKey, auth, appId, key, &encryptedKey)) return false; + if (!writeStringToFile(encryptedKey, dir + "/" + kFn_encrypted_key)) return false; + return true; +}*/ + +bool retrieveKey(const std::string& dir, const KeyAuthentication& auth, std::string* key) { + std::string version; + if (!readFileToString(dir + "/" + kFn_version, &version)) return false; + if (version != kCurrentVersion) { + LOG(ERROR) << "Version mismatch, expected " << kCurrentVersion << " got " << version; + return false; + } + std::string secdiscardable; + if (!readFileToString(dir + "/" + kFn_secdiscardable, &secdiscardable)) return false; + std::string stretching; + if (!readFileToString(dir + "/" + kFn_stretching, &stretching)) return false; + std::string salt; + if (stretchingNeedsSalt(stretching)) { + if (!readFileToString(dir + "/" + kFn_salt, &salt)) return false; + } + std::string appId; + if (!generateAppId(auth, stretching, salt, secdiscardable, &appId)) return false; + std::string kmKey; + if (!readFileToString(dir + "/" + kFn_keymaster_key_blob, &kmKey)) return false; + std::string encryptedMessage; + if (!readFileToString(dir + "/" + kFn_encrypted_key, &encryptedMessage)) return false; + Keymaster keymaster; + if (!keymaster) return false; + return decryptWithKeymasterKey(keymaster, kmKey, auth, appId, encryptedMessage, key); +} + +static bool deleteKey(const std::string& dir) { + std::string kmKey; + if (!readFileToString(dir + "/" + kFn_keymaster_key_blob, &kmKey)) return false; + Keymaster keymaster; + if (!keymaster) return false; + if (!keymaster.deleteKey(kmKey)) return false; + return true; +} + +static bool secdiscardSecdiscardable(const std::string& dir) { + if (ForkExecvp( + std::vector<std::string>{kSecdiscardPath, "--", dir + "/" + kFn_secdiscardable}) != 0) { + LOG(ERROR) << "secdiscard failed"; + return false; + } + return true; +} + +static bool recursiveDeleteKey(const std::string& dir) { + if (ForkExecvp(std::vector<std::string>{kRmPath, "-rf", dir}) != 0) { + LOG(ERROR) << "recursive delete failed"; + return false; + } + return true; +} + +bool destroyKey(const std::string& dir) { + bool success = true; + // Try each thing, even if previous things failed. + success &= deleteKey(dir); + success &= secdiscardSecdiscardable(dir); + success &= recursiveDeleteKey(dir); + return success; +} + +} // namespace vold +} // namespace android 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/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/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..8d0445d89 --- /dev/null +++ b/crypto/ext4crypt/Utils.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TWRP_VOLD_UTILS_H +#define TWRP_VOLD_UTILS_H + +#include <utils/Errors.h> +#include <cutils/multiuser.h> +#include <selinux/selinux.h> + +#include <vector> +#include <string> + +namespace android { +namespace vold { + +/* Returns either WEXITSTATUS() status, or a negative errno */ +status_t ForkExecvp(const std::vector<std::string>& args); +status_t ForkExecvp(const std::vector<std::string>& args, security_context_t context); + +status_t ForkExecvp(const std::vector<std::string>& args, + std::vector<std::string>& output); +status_t ForkExecvp(const std::vector<std::string>& args, + std::vector<std::string>& output, security_context_t context); + +pid_t ForkExecvpAsync(const std::vector<std::string>& args); + +status_t ReadRandomBytes(size_t bytes, std::string& out); + +/* Converts hex string to raw bytes, ignoring [ :-] */ +status_t HexToStr(const std::string& hex, std::string& str); + +std::string BuildKeyPath(const std::string& partGuid); + +std::string BuildDataSystemLegacyPath(userid_t userid); +std::string BuildDataSystemCePath(userid_t userid); +std::string BuildDataSystemDePath(userid_t userid); +std::string BuildDataMiscLegacyPath(userid_t userid); +std::string BuildDataMiscCePath(userid_t userid); +std::string BuildDataMiscDePath(userid_t userid); +std::string BuildDataProfilesDePath(userid_t userid); +std::string BuildDataProfilesForeignDexDePath(userid_t userid); + +std::string BuildDataPath(const char* volumeUuid); +std::string BuildDataMediaCePath(const char* volumeUuid, userid_t userid); +std::string BuildDataUserCePath(const char* volumeUuid, userid_t userid); +std::string BuildDataUserDePath(const char* volumeUuid, userid_t userid); + +} // namespace vold +} // namespace android + +#endif diff --git a/crypto/ext4crypt/e4policyget.cpp b/crypto/ext4crypt/e4policyget.cpp new file mode 100644 index 000000000..d217f18ee --- /dev/null +++ b/crypto/ext4crypt/e4policyget.cpp @@ -0,0 +1,44 @@ +/* + * 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 { + char e4crypt_policy[EXT4_KEY_DESCRIPTOR_SIZE]; + if (e4crypt_policy_get(argv[1], e4crypt_policy, EXT4_KEY_DESCRIPTOR_SIZE, 0)) + { + char* ptr = tar_policy; + memset(tar_policy, 0, sizeof(tar_policy)); + char policy_hex[EXT4_KEY_DESCRIPTOR_SIZE_HEX]; + policy_to_hex(e4crypt_policy, 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..029db7567 --- /dev/null +++ b/crypto/ext4crypt/ext4_crypt.cpp @@ -0,0 +1,180 @@ +/* + * 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 <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)'.') + +// ext4enc:TODO Include structure from somewhere sensible +// MUST be in sync with ext4_crypto.c in kernel +#define EXT4_KEY_DESCRIPTOR_SIZE 8 +#define EXT4_KEY_DESCRIPTOR_SIZE_HEX 17 + +struct ext4_encryption_policy { + char version; + char contents_encryption_mode; + char filenames_encryption_mode; + char flags; + char master_key_descriptor[EXT4_KEY_DESCRIPTOR_SIZE]; +} __attribute__((__packed__)); + +#define EXT4_ENCRYPTION_MODE_AES_256_XTS 1 +#define EXT4_ENCRYPTION_MODE_AES_256_CTS 4 +#define EXT4_ENCRYPTION_MODE_PRIVATE 127 + +static int encryption_mode = EXT4_ENCRYPTION_MODE_PRIVATE; + +// ext4enc:TODO Get value from somewhere sensible +#define EXT4_IOC_SET_ENCRYPTION_POLICY _IOR('f', 19, struct ext4_encryption_policy) +#define EXT4_IOC_GET_ENCRYPTION_POLICY _IOW('f', 21, struct ext4_encryption_policy) + +#define HEX_LOOKUP "0123456789abcdef" + +extern "C" void policy_to_hex(const char* policy, char* hex) { + 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" 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..1c9cef0a5 --- /dev/null +++ b/crypto/ext4crypt/ext4crypt_tar.h @@ -0,0 +1,38 @@ +/* + * 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> + +__BEGIN_DECLS + +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); + +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..f0822063e --- /dev/null +++ b/crypto/lollipop/cryptfs.c @@ -0,0 +1,1595 @@ +/* + * 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(¶ms, '\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, ¶ms, + &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, + ¶ms, + 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, ¶m_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(¶ms, '\0', sizeof(params)); + params.public_exponent = RSA_EXPONENT; + params.modulus_size = RSA_KEY_SIZE; + + if (keymaster0_dev->generate_keypair(keymaster0_dev, TYPE_RSA, ¶ms, + &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, + ¶ms, + 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, + ¶m_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, + ¶m_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"); + 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"); + 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..e371c24e7 --- /dev/null +++ b/crypto/vold_decrypt/Android.mk @@ -0,0 +1,87 @@ +# 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),) + ifneq ($(TW_CRYPTO_USE_SYSTEM_VOLD),false) + + + # 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; \ + ) + 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),) + LOCAL_CFLAGS += -DTW_CRYPTO_SYSTEM_VOLD_SERVICES='"$(services)"' + 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 + + ifeq ($(TW_CRYPTO_SYSTEM_VOLD_DISABLE_TIMEOUT),true) + LOCAL_CFLAGS += -DTW_CRYPTO_SYSTEM_VOLD_DISABLE_TIMEOUT + endif + + LOCAL_SRC_FILES = vold_decrypt.cpp + LOCAL_SHARED_LIBRARIES := libcutils + include $(BUILD_STATIC_LIBRARY) + + endif + endif +endif 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..06bdebcd3 --- /dev/null +++ b/crypto/vold_decrypt/init.recovery.vold_decrypt.qseecomd.rc @@ -0,0 +1,14 @@ +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 + +service sys_qseecomd /system/bin/qseecomd + user root + group root + setenv PATH /system/bin + setenv LD_LIBRARY_PATH /system/lib64:/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..d535a2514 --- /dev/null +++ b/crypto/vold_decrypt/vold_decrypt.cpp @@ -0,0 +1,598 @@ +/* + 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 <sys/time.h> + +#ifdef TW_CRYPTO_SYSTEM_VOLD_DEBUG +#include <signal.h> +#include <sys/types.h> +#include <sys/wait.h> +#endif + +#include <string> +#include <vector> +#include <sstream> + +#include "../../twcommon.h" +#include "../../partitions.hpp" +#include "../../twrp-functions.hpp" +#include "../../gui/gui.hpp" + +using namespace std; + +extern "C" { + #include <cutils/properties.h> +} + +/* 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 */ + + +#define LOGDECRYPT(...) do { printf(__VA_ARGS__); if (fp_kmsg) { fprintf(fp_kmsg, "[VOLD_DECRYPT]" __VA_ARGS__); fflush(fp_kmsg); } } while (0) +#define LOGDECRYPT_KMSG(...) do { if (fp_kmsg) { fprintf(fp_kmsg, "[VOLD_DECRYPT]" __VA_ARGS__); fflush(fp_kmsg); } } while (0) + +#ifdef TW_CRYPTO_SYSTEM_VOLD_SERVICES +typedef struct { + string service_name; + string twrp_svc_name; + bool is_running; + bool resume; +} AdditionalService; +#endif + +FILE *fp_kmsg = NULL; +bool has_timeout = false; + +#ifdef TW_CRYPTO_SYSTEM_VOLD_DEBUG +bool has_strace = false; + +pid_t strace_init(void) { + if (!has_strace) + return -1; + + pid_t pid; + switch(pid = fork()) + { + case -1: + LOGDECRYPT_KMSG("forking strace_init failed: %d!\n", errno); + return -1; + case 0: // child + execl("/sbin/strace", "strace", "-q", "-tt", "-ff", "-v", "-y", "-s", "1000", "-o", "/tmp/strace_init.log", "-p", "1" , NULL); + LOGDECRYPT_KMSG("strace_init fork failed: %d!\n", errno); + exit(-1); + default: + LOGDECRYPT_KMSG("Starting strace_init (pid=%d)\n", pid); + return pid; + } +} +#endif + +/* 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(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; +} + +string wait_for_property(string property_name, int utimeout = SLEEP_MAX_USEC, 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; + LOGDECRYPT_KMSG("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; + LOGDECRYPT_KMSG("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; +} + +bool Service_Exists(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 (strcmp(prop_value, "error") != 0); +} + +bool Is_Service_Running(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 (strcmp(prop_value, "running") == 0); +} + +bool Is_Service_Stopped(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 (strcmp(prop_value, "stopped") == 0); +} + +bool Start_Service(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"); + + LOGDECRYPT("Start service %s: %s.\n", initrc_svc.c_str(), res.c_str()); + + return (res == "running"); +} + +bool Stop_Service(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"); + LOGDECRYPT("Stop service %s: %s.\n", initrc_svc.c_str(), res.c_str()); + } + + return (res == "stopped"); +} + +void output_dmesg_to_recoverylog(void) { + TWFunc::Exec_Cmd( + "echo \"---- DMESG LOG FOLLOWS ----\";" + "dmesg | grep 'DECRYPT\\|vold\\|qseecom\\|QSEECOM\\|keymaste\\|keystore\\|cmnlib';" + "echo \"---- DMESG LOG ENDS ----\"" + ); +} + +void set_needed_props(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()); + } +} + +string vdc_cryptfs_cmd(string log_name) { + string cmd = "LD_LIBRARY_PATH=/system/lib64:/system/lib /system/bin/vdc cryptfs"; + +#ifndef TW_CRYPTO_SYSTEM_VOLD_DEBUG + (void)log_name; // do nothing, but get rid of compiler warning in non debug builds +#else + if (has_timeout && has_strace) + cmd = "/sbin/strace -q -tt -ff -v -y -s 1000 -o /tmp/strace_vdc_" + log_name + " /sbin/timeout -t 30 -s KILL env " + cmd; + else if (has_strace) + cmd = "/sbin/strace -q -tt -ff -v -y -s 1000 -o /tmp/strace_vdc_" + log_name + " -E " + cmd; + else +#endif + if (has_timeout) + cmd = "/sbin/timeout -t 30 -s KILL env " + cmd; + + return cmd; +} + +int run_vdc(string Password) { + int res = -1; + struct timeval t1, t2; + string vdc_res; + int vdc_r1, vdc_r2, vdc_r3; + + LOGDECRYPT("About to run vdc...\n"); + + // Wait for vold connection + gettimeofday(&t1, NULL); + t2 = t1; + while ((t2.tv_sec - t1.tv_sec) < 5) { + vdc_res.clear(); + // cryptfs getpwtype returns: R1=213(PasswordTypeResult) R2=? R3="password", "pattern", "pin", "default" + res = TWFunc::Exec_Cmd(vdc_cryptfs_cmd("connect") + " getpwtype", vdc_res); + std::replace(vdc_res.begin(), vdc_res.end(), '\n', ' '); // remove newline(s) + vdc_r1 = vdc_r2 = vdc_r3 = -1; + sscanf(vdc_res.c_str(), "%d", &vdc_r1); + if (vdc_r1 == 213) { + char str_res[sizeof(int) + 1]; + snprintf(str_res, sizeof(str_res), "%d", res); + vdc_res += "ret="; + vdc_res += str_res; + res = 0; + break; + } + LOGDECRYPT("Retrying connection to vold\n"); + usleep(SLEEP_MIN_USEC); // vdc usually usleep(10000), but that causes too many unnecessary attempts + gettimeofday(&t2, NULL); + } + + if (res != 0) + return res; + + LOGDECRYPT("Connected to vold (%s)\n", vdc_res.c_str()); + + // Input password from GUI, or default password + vdc_res.clear(); + res = TWFunc::Exec_Cmd(vdc_cryptfs_cmd("passwd") + " checkpw '" + Password + "'", vdc_res); + std::replace(vdc_res.begin(), vdc_res.end(), '\n', ' '); // remove newline(s) + LOGDECRYPT("vdc cryptfs result (passwd): %s (ret=%d)\n", vdc_res.c_str(), res); + vdc_r1 = vdc_r2 = vdc_r3 = -1; + sscanf(vdc_res.c_str(), "%d %d %d", &vdc_r1, &vdc_r2, &vdc_r3); + + if (vdc_r3 != 0) { + // try falling back to Lollipop hex passwords + string hexPassword = convert_key_to_hex_ascii(Password); + vdc_res.clear(); + res = TWFunc::Exec_Cmd(vdc_cryptfs_cmd("hex_pw") + " checkpw '" + hexPassword + "'", vdc_res); + std::replace(vdc_res.begin(), vdc_res.end(), '\n', ' '); // remove newline(s) + LOGDECRYPT("vdc cryptfs result (hex_pw): %s (ret=%d)\n", vdc_res.c_str(), res); + vdc_r1 = vdc_r2 = vdc_r3 = -1; + sscanf(vdc_res.c_str(), "%d %d %d", &vdc_r1, &vdc_r2, &vdc_r3); + } + + // vdc's return value is dependant upon source origin, it will either + // return 0 or vdc_r1, so disregard and focus on decryption instead + if (vdc_r3 == 0) { + // Decryption successful wait for crypto blk dev + wait_for_property("ro.crypto.fs_crypto_blkdev"); + res = 0; + } else { + res = -1; + } + + return res; +} + +bool Symlink_Vendor_Folder(void) { + bool is_vendor_symlinked = false; + + if (PartitionManager.Is_Mounted_By_Path("/vendor")) { + LOGDECRYPT("vendor partition mounted, skipping /vendor substitution\n"); + } + else if (TWFunc::Path_Exists("/system/vendor")) { + LOGDECRYPT("Symlinking vendor folder...\n"); + if (TWFunc::Path_Exists("/vendor") && rename("/vendor", "/vendor-orig") != 0) { + LOGDECRYPT("Failed to rename original /vendor folder: %s\n", strerror(errno)); + } else { + TWFunc::Recursive_Mkdir("/vendor/firmware/keymaster"); + LOGDECRYPT_KMSG("Symlinking /system/vendor/lib64 to /vendor/lib64 (res=%d)\n", + symlink("/system/vendor/lib64", "/vendor/lib64") + ); + LOGDECRYPT_KMSG("Symlinking /system/vendor/lib to /vendor/lib (res=%d)\n", + symlink("/system/vendor/lib", "/vendor/lib") + ); + 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); + rename("/vendor-orig", "/vendor"); +} + +bool Symlink_Firmware_Folder(void) { + bool is_firmware_symlinked = false; + + if (PartitionManager.Is_Mounted_By_Path("/firmware")) { + LOGDECRYPT("firmware partition mounted, skipping /firmware substitution\n"); + } else { + LOGDECRYPT("Symlinking firmware folder...\n"); + if (TWFunc::Path_Exists("/firmware") && rename("/firmware", "/firmware-orig") != 0) { + LOGDECRYPT("Failed to rename original /firmware folder: %s\n", strerror(errno)); + } else { + 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); + rename("/firmware-orig", "/firmware"); +} + +void Symlink_Firmware_Files(bool is_vendor_symlinked, bool is_firmware_symlinked) { + if (!is_vendor_symlinked && !is_firmware_symlinked) + return; + + LOGDECRYPT("Symlinking firmware files...\n"); + string result_of_find; + TWFunc::Exec_Cmd("find /system -name keymaste*.* -type f -o -name cmnlib.* -type f 2>/dev/null", result_of_find); + + stringstream ss(result_of_find); + string line; + int count = 0; + + while(getline(ss, line)) { + const char *fwfile = line.c_str(); + string base_name = TWFunc::Get_Filename(line); + count++; + + if (is_firmware_symlinked) { + LOGDECRYPT_KMSG("Symlinking %s to /firmware/image/ (res=%d)\n", fwfile, + symlink(fwfile, ("/firmware/image/" + base_name).c_str()) + ); + } + + if (is_vendor_symlinked) { + LOGDECRYPT_KMSG("Symlinking %s to /vendor/firmware/ (res=%d)\n", fwfile, + symlink(fwfile, ("/vendor/firmware/" + base_name).c_str()) + ); + + LOGDECRYPT_KMSG("Symlinking %s to /vendor/firmware/keymaster/ (res=%d)\n", fwfile, + symlink(fwfile, ("/vendor/firmware/keymaster/" + base_name).c_str()) + ); + } + } + LOGDECRYPT("%d file(s) symlinked.\n", count); +} + +#ifdef TW_CRYPTO_SYSTEM_VOLD_SERVICES +vector<AdditionalService> Get_List_Of_Additional_Services (void) { + vector<AdditionalService> services; + + 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]; + services.push_back(svc); + } + + return services; +} +#endif + +int vold_decrypt(string Password) +{ + int res; + bool output_dmesg_to_log = false; + bool is_vendor_symlinked = false; + bool is_firmware_symlinked = false; + bool is_vold_running = false; + + if (Password.empty()) { + LOGDECRYPT("vold_decrypt: password is empty!\n"); + return -1; + } + + // Mount system and check for vold and vdc + if (!PartitionManager.Mount_By_Path("/system", true)) { + return -1; + } else if (!TWFunc::Path_Exists("/system/bin/vold")) { + LOGDECRYPT("ERROR: /system/bin/vold not found, aborting.\n"); + gui_msg(Msg(msg::kError, "decrypt_data_vold_os_missing=Missing files needed for vold decrypt: {1}")("/system/bin/vold")); + return -1; + } else if (!TWFunc::Path_Exists("/system/bin/vdc")) { + LOGDECRYPT("ERROR: /system/bin/vdc not found, aborting.\n"); + gui_msg(Msg(msg::kError, "decrypt_data_vold_os_missing=Missing files needed for vold decrypt: {1}")("/system/bin/vdc")); + return -1; + } + + fp_kmsg = fopen("/dev/kmsg", "a"); + + LOGDECRYPT("TW_CRYPTO_USE_SYSTEM_VOLD := true\n"); + LOGDECRYPT("Attempting to use system's vold for decryption...\n"); + +#ifndef TW_CRYPTO_SYSTEM_VOLD_DISABLE_TIMEOUT + has_timeout = TWFunc::Path_Exists("/sbin/timeout"); + if (!has_timeout) + LOGDECRYPT("timeout binary not found, disabling timeout in vold_decrypt!\n"); +#endif + +#ifdef TW_CRYPTO_SYSTEM_VOLD_DEBUG + has_strace = TWFunc::Path_Exists("/sbin/strace"); + if (!has_strace) + LOGDECRYPT("strace binary not found, disabling strace in vold_decrypt!\n"); + pid_t pid_strace = strace_init(); +#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 (Service_Exists(Services[i].service_name)) + Services[i].twrp_svc_name = Services[i].service_name; + else if (Service_Exists("sbin" + Services[i].service_name)) + Services[i].twrp_svc_name = "sbin" + Services[i].service_name; + else + Services[i].twrp_svc_name.clear(); + + if (!Services[i].twrp_svc_name.empty() && !Is_Service_Stopped(Services[i].twrp_svc_name)) { + Services[i].resume = true; + Stop_Service(Services[i].twrp_svc_name); + } else + Services[i].resume = false; + + // vold_decrypt system services have to be named sys_{service} in the .rc files + Services[i].service_name = "sys_" + Services[i].service_name; + } +#endif + + LOGDECRYPT("Setting up folders and permissions...\n"); + is_vendor_symlinked = Symlink_Vendor_Folder(); + is_firmware_symlinked = Symlink_Firmware_Folder(); + Symlink_Firmware_Files(is_vendor_symlinked, is_firmware_symlinked); + + set_needed_props(); + + // Start services needed for vold decrypt + LOGDECRYPT("Starting services...\n"); +#ifdef TW_CRYPTO_SYSTEM_VOLD_SERVICES + for (size_t i = 0; i < Services.size(); ++i) { + Services[i].is_running = Start_Service(Services[i].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 (!Is_Service_Running(Services[i].service_name) && Services[i].resume) { + // if system_service has died restart the twrp_service + LOGDECRYPT("%s is not running, resuming %s!\n", Services[i].service_name.c_str(), Services[i].twrp_svc_name.c_str()); + Start_Service(Services[i].twrp_svc_name); + } + } +#endif + + res = run_vdc(Password); + + if (res != 0) { + // Decryption was unsuccessful + LOGDECRYPT("Decryption failed\n"); + output_dmesg_to_log = true; + } + } else { + LOGDECRYPT("Failed to start vold\n"); + TWFunc::Exec_Cmd("echo \"$(getprop | grep init.svc)\" >> /dev/kmsg"); + output_dmesg_to_log = true; + } + + // Stop services needed for vold decrypt so /system can be unmounted + LOGDECRYPT("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].service_name) && Services[i].resume) + Stop_Service(Services[i].twrp_svc_name); + else + Stop_Service(Services[i].service_name); + } +#endif + + if (is_firmware_symlinked) + Restore_Firmware_Folder(); + if (is_vendor_symlinked) + Restore_Vendor_Folder(); + + 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 + LOGDECRYPT("WARNING: system could not be unmounted normally!\n"); + TWFunc::Exec_Cmd("umount -l /system"); + } + + LOGDECRYPT("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_svc_name); + } +#endif + +#ifdef TW_CRYPTO_SYSTEM_VOLD_DEBUG + if (pid_strace > 0) { + LOGDECRYPT_KMSG("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) + LOGDECRYPT_KMSG("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) + LOGDECRYPT_KMSG("strace_init killed successfully\n"); + else + LOGDECRYPT_KMSG("strace_init took too long to kill, may be a zombie process\n"); + } + } + output_dmesg_to_log = true; +#endif + + // Finish up and exit + if (fp_kmsg) { + fflush(fp_kmsg); + fclose(fp_kmsg); + } + + if (output_dmesg_to_log) + output_dmesg_to_recoverylog(); + + // Finally check if crypto device is up + if (wait_for_property("ro.crypto.fs_crypto_blkdev", 0) != "error") + res = 0; + else + res = -1; + + return res; +} diff --git a/crypto/vold_decrypt/vold_decrypt.h b/crypto/vold_decrypt/vold_decrypt.h new file mode 100644 index 000000000..70db404e9 --- /dev/null +++ b/crypto/vold_decrypt/vold_decrypt.h @@ -0,0 +1,26 @@ +/* + 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> + +int vold_decrypt(std::string Password); + +#endif // _VOLD_DECRYPT_H |