From 26a53888a4efa4a966db189dd0b614f7573b2760 Mon Sep 17 00:00:00 2001 From: Paul Crowley Date: Thu, 26 Oct 2017 11:16:39 -0700 Subject: [PATCH] When we forget a volume, forget per-volume key Protect all per-volume-per-user keys with a per-volume key, which is forgotten when the volume is forgotten. This means that the user's key is securely lost even when their storage is encrypted at forgetting time. Bug: 25861755 Test: create a volume, forget it, check logs and filesystem. Change-Id: I8df77bc91bbfa2258e082ddd54d6160dbf39b378 --- Ext4Crypt.cpp | 29 ++++++++++++++++++++--- KeyStorage.cpp | 63 +++++++++++++++++++++++++++++++------------------- KeyStorage.h | 3 +++ KeyUtil.cpp | 10 ++++---- KeyUtil.h | 6 +++-- 5 files changed, 77 insertions(+), 34 deletions(-) diff --git a/Ext4Crypt.cpp b/Ext4Crypt.cpp index 495a0fa..85ace4a 100644 --- a/Ext4Crypt.cpp +++ b/Ext4Crypt.cpp @@ -79,6 +79,9 @@ const std::string user_key_dir = std::string() + DATA_MNT_POINT + "/misc/vold/us const std::string user_key_temp = user_key_dir + "/temp"; const std::string prepare_subdirs_path = "/system/bin/vold_prepare_subdirs"; +const std::string systemwide_volume_key_dir = + std::string() + DATA_MNT_POINT + "/misc/vold/volume_keys"; + bool s_global_de_initialized = false; // Some users are ephemeral, don't try to wipe their keys from disk @@ -336,8 +339,8 @@ bool e4crypt_initialize_global_de() { } PolicyKeyRef device_ref; - if (!android::vold::retrieveAndInstallKey(true, device_key_path, device_key_temp, - &device_ref.key_raw_ref)) + if (!android::vold::retrieveAndInstallKey(true, kEmptyAuthentication, device_key_path, + device_key_temp, &device_ref.key_raw_ref)) return false; get_data_file_encryption_modes(&device_ref); @@ -503,14 +506,32 @@ static std::string volkey_path(const std::string& misc_path, const std::string& return misc_path + "/vold/volume_keys/" + volume_uuid + "/default"; } +static std::string volume_secdiscardable_path(const std::string& volume_uuid) { + return systemwide_volume_key_dir + "/" + volume_uuid + "/secdiscardable"; +} + static bool read_or_create_volkey(const std::string& misc_path, const std::string& volume_uuid, PolicyKeyRef* key_ref) { + auto secdiscardable_path = volume_secdiscardable_path(volume_uuid); + std::string secdiscardable_hash; + if (android::vold::pathExists(secdiscardable_path)) { + if (!android::vold::readSecdiscardable(secdiscardable_path, &secdiscardable_hash)) + return false; + } else { + if (fs_mkdirs(secdiscardable_path.c_str(), 0700) != 0) { + PLOG(ERROR) << "Creating directories for: " << secdiscardable_path; + return false; + } + if (!android::vold::createSecdiscardable(secdiscardable_path, &secdiscardable_hash)) + return false; + } auto key_path = volkey_path(misc_path, volume_uuid); if (fs_mkdirs(key_path.c_str(), 0700) != 0) { PLOG(ERROR) << "Creating directories for: " << key_path; return false; } - if (!android::vold::retrieveAndInstallKey(true, key_path, key_path + "_tmp", + android::vold::KeyAuthentication auth("", secdiscardable_hash); + if (!android::vold::retrieveAndInstallKey(true, auth, key_path, key_path + "_tmp", &key_ref->key_raw_ref)) return false; key_ref->contents_mode = @@ -798,6 +819,8 @@ static bool destroy_volume_keys(const std::string& directory_path, const std::st bool e4crypt_destroy_volume_keys(const std::string& volume_uuid) { bool res = true; LOG(DEBUG) << "e4crypt_destroy_volume_keys for volume " << escape_empty(volume_uuid); + auto secdiscardable_path = volume_secdiscardable_path(volume_uuid); + res &= android::vold::runSecdiscardSingle(secdiscardable_path); res &= destroy_volume_keys("/data/misc_ce", volume_uuid); res &= destroy_volume_keys("/data/misc_de", volume_uuid); return res; diff --git a/KeyStorage.cpp b/KeyStorage.cpp index 143272d..8878a3c 100644 --- a/KeyStorage.cpp +++ b/KeyStorage.cpp @@ -88,7 +88,7 @@ static bool checkSize(const std::string& kind, size_t actual, size_t expected) { return true; } -static std::string hashWithPrefix(char const* prefix, const std::string& tohash) { +static void hashWithPrefix(char const* prefix, const std::string& tohash, std::string* res) { SHA512_CTX c; SHA512_Init(&c); @@ -99,9 +99,8 @@ static std::string hashWithPrefix(char const* prefix, const std::string& tohash) hashingPrefix.resize(SHA512_CBLOCK); SHA512_Update(&c, hashingPrefix.data(), hashingPrefix.size()); SHA512_Update(&c, tohash.data(), tohash.size()); - std::string res(SHA512_DIGEST_LENGTH, '\0'); - SHA512_Final(reinterpret_cast(&res[0]), &c); - return res; + res->assign(SHA512_DIGEST_LENGTH, '\0'); + SHA512_Final(reinterpret_cast(&(*res)[0]), &c); } static bool generateKeymasterKey(Keymaster& keymaster, const KeyAuthentication& auth, @@ -160,6 +159,30 @@ static bool writeStringToFile(const std::string& payload, const std::string& fil return true; } +static bool readRandomBytesOrLog(size_t count, std::string* out) { + auto status = ReadRandomBytes(count, *out); + if (status != OK) { + LOG(ERROR) << "Random read failed with status: " << status; + return false; + } + return true; +} + +bool createSecdiscardable(const std::string& filename, std::string* hash) { + std::string secdiscardable; + if (!readRandomBytesOrLog(SECDISCARDABLE_BYTES, &secdiscardable)) return false; + if (!writeStringToFile(secdiscardable, filename)) return false; + hashWithPrefix(kHashPrefix_secdiscardable, secdiscardable, hash); + return true; +} + +bool readSecdiscardable(const std::string& filename, std::string* hash) { + std::string secdiscardable; + if (!readFileToString(filename, &secdiscardable)) return false; + hashWithPrefix(kHashPrefix_secdiscardable, secdiscardable, hash); + return true; +} + static KeymasterOperation begin(Keymaster& keymaster, const std::string& dir, KeyPurpose purpose, const AuthorizationSet &keyParams, @@ -283,20 +306,11 @@ static bool stretchSecret(const std::string& stretching, const std::string& secr } static bool generateAppId(const KeyAuthentication& auth, const std::string& stretching, - const std::string& salt, const std::string& secdiscardable, + const std::string& salt, const std::string& secdiscardable_hash, std::string* appId) { std::string stretched; if (!stretchSecret(stretching, auth.secret, salt, &stretched)) return false; - *appId = hashWithPrefix(kHashPrefix_secdiscardable, secdiscardable) + stretched; - return true; -} - -static bool readRandomBytesOrLog(size_t count, std::string* out) { - auto status = ReadRandomBytes(count, *out); - if (status != OK) { - LOG(ERROR) << "Random read failed with status: " << status; - return false; - } + *appId = secdiscardable_hash + stretched; return true; } @@ -306,7 +320,8 @@ static void logOpensslError() { static bool encryptWithoutKeymaster(const std::string& preKey, const KeyBuffer& plaintext, std::string* ciphertext) { - auto key = hashWithPrefix(kHashPrefix_keygen, preKey); + std::string key; + hashWithPrefix(kHashPrefix_keygen, preKey, &key); key.resize(AES_KEY_BYTES); if (!readRandomBytesOrLog(GCM_NONCE_BYTES, ciphertext)) return false; auto ctx = std::unique_ptr( @@ -356,7 +371,8 @@ static bool decryptWithoutKeymaster(const std::string& preKey, LOG(ERROR) << "GCM ciphertext too small: " << ciphertext.size(); return false; } - auto key = hashWithPrefix(kHashPrefix_keygen, preKey); + std::string key; + hashWithPrefix(kHashPrefix_keygen, preKey, &key); key.resize(AES_KEY_BYTES); auto ctx = std::unique_ptr( EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free); @@ -410,9 +426,8 @@ bool storeKey(const std::string& dir, const KeyAuthentication& auth, const KeyBu return false; } if (!writeStringToFile(kCurrentVersion, dir + "/" + kFn_version)) return false; - std::string secdiscardable; - if (!readRandomBytesOrLog(SECDISCARDABLE_BYTES, &secdiscardable)) return false; - if (!writeStringToFile(secdiscardable, dir + "/" + kFn_secdiscardable)) return false; + std::string secdiscardable_hash; + if (!createSecdiscardable(dir + "/" + kFn_secdiscardable, &secdiscardable_hash)) return false; std::string stretching = getStretching(auth); if (!writeStringToFile(stretching, dir + "/" + kFn_stretching)) return false; std::string salt; @@ -424,7 +439,7 @@ bool storeKey(const std::string& dir, const KeyAuthentication& auth, const KeyBu if (!writeStringToFile(salt, dir + "/" + kFn_salt)) return false; } std::string appId; - if (!generateAppId(auth, stretching, salt, secdiscardable, &appId)) return false; + if (!generateAppId(auth, stretching, salt, secdiscardable_hash, &appId)) return false; std::string encryptedKey; if (auth.usesKeymaster()) { Keymaster keymaster; @@ -467,8 +482,8 @@ bool retrieveKey(const std::string& dir, const KeyAuthentication& auth, KeyBuffe LOG(ERROR) << "Version mismatch, expected " << kCurrentVersion << " got " << version; return false; } - std::string secdiscardable; - if (!readFileToString(dir + "/" + kFn_secdiscardable, &secdiscardable)) return false; + std::string secdiscardable_hash; + if (!readSecdiscardable(dir + "/" + kFn_secdiscardable, &secdiscardable_hash)) return false; std::string stretching; if (!readFileToString(dir + "/" + kFn_stretching, &stretching)) return false; std::string salt; @@ -476,7 +491,7 @@ bool retrieveKey(const std::string& dir, const KeyAuthentication& auth, KeyBuffe if (!readFileToString(dir + "/" + kFn_salt, &salt)) return false; } std::string appId; - if (!generateAppId(auth, stretching, salt, secdiscardable, &appId)) return false; + if (!generateAppId(auth, stretching, salt, secdiscardable_hash, &appId)) return false; std::string encryptedMessage; if (!readFileToString(dir + "/" + kFn_encrypted_key, &encryptedMessage)) return false; if (auth.usesKeymaster()) { diff --git a/KeyStorage.h b/KeyStorage.h index 655cd17..786e5b4 100644 --- a/KeyStorage.h +++ b/KeyStorage.h @@ -44,6 +44,9 @@ extern const KeyAuthentication kEmptyAuthentication; // Checks if path "path" exists. bool pathExists(const std::string& path); +bool createSecdiscardable(const std::string& path, std::string* hash); +bool readSecdiscardable(const std::string& path, std::string* hash); + // 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. diff --git a/KeyUtil.cpp b/KeyUtil.cpp index dbc73c1..9885440 100644 --- a/KeyUtil.cpp +++ b/KeyUtil.cpp @@ -161,12 +161,13 @@ bool evictKey(const std::string& raw_ref) { return success; } -bool retrieveAndInstallKey(bool create_if_absent, const std::string& key_path, - const std::string& tmp_path, std::string* key_ref) { +bool retrieveAndInstallKey(bool create_if_absent, const KeyAuthentication& key_authentication, + const std::string& key_path, const std::string& tmp_path, + std::string* key_ref) { KeyBuffer key; if (pathExists(key_path)) { LOG(DEBUG) << "Key exists, using: " << key_path; - if (!retrieveKey(key_path, kEmptyAuthentication, &key)) return false; + if (!retrieveKey(key_path, key_authentication, &key)) return false; } else { if (!create_if_absent) { LOG(ERROR) << "No key found in " << key_path; @@ -174,8 +175,7 @@ bool retrieveAndInstallKey(bool create_if_absent, const std::string& key_path, } LOG(INFO) << "Creating new key in " << key_path; if (!randomKey(&key)) return false; - if (!storeKeyAtomically(key_path, tmp_path, - kEmptyAuthentication, key)) return false; + if (!storeKeyAtomically(key_path, tmp_path, key_authentication, key)) return false; } if (!installKey(key, key_ref)) { diff --git a/KeyUtil.h b/KeyUtil.h index 412b0ae..a85eca1 100644 --- a/KeyUtil.h +++ b/KeyUtil.h @@ -18,6 +18,7 @@ #define ANDROID_VOLD_KEYUTIL_H #include "KeyBuffer.h" +#include "KeyStorage.h" #include #include @@ -28,8 +29,9 @@ namespace vold { bool randomKey(KeyBuffer* key); bool installKey(const KeyBuffer& key, std::string* raw_ref); bool evictKey(const std::string& raw_ref); -bool retrieveAndInstallKey(bool create_if_absent, const std::string& key_path, - const std::string& tmp_path, std::string* key_ref); +bool retrieveAndInstallKey(bool create_if_absent, const KeyAuthentication& key_authentication, + const std::string& key_path, const std::string& tmp_path, + std::string* key_ref); bool retrieveKey(bool create_if_absent, const std::string& key_path, const std::string& tmp_path, KeyBuffer* key);