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
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_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 @@
     }
 
     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 @@
     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 @@
 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 @@
     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 @@
     hashingPrefix.resize(SHA512_CBLOCK);
     SHA512_Update(&c, hashingPrefix.data(), hashingPrefix.size());
     SHA512_Update(&c, tohash.data(), tohash.size());
-    std::string res(SHA512_DIGEST_LENGTH, '\0');
-    SHA512_Final(reinterpret_cast<uint8_t*>(&res[0]), &c);
-    return res;
+    res->assign(SHA512_DIGEST_LENGTH, '\0');
+    SHA512_Final(reinterpret_cast<uint8_t*>(&(*res)[0]), &c);
 }
 
 static bool generateKeymasterKey(Keymaster& keymaster, const KeyAuthentication& auth,
@@ -160,6 +159,30 @@
     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 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 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<EVP_CIPHER_CTX, decltype(&::EVP_CIPHER_CTX_free)>(
@@ -356,7 +371,8 @@
         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, decltype(&::EVP_CIPHER_CTX_free)>(
         EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free);
@@ -410,9 +426,8 @@
         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 @@
         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 @@
         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 @@
         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 @@
 // 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 @@
     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 @@
         }
         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 <string>
 #include <memory>
@@ -28,8 +29,9 @@
 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);