Merge "Retry deleting dm devices."
diff --git a/Android.bp b/Android.bp
index 1646c42..a420078 100644
--- a/Android.bp
+++ b/Android.bp
@@ -112,6 +112,7 @@
         "Benchmark.cpp",
         "CheckEncryption.cpp",
         "Checkpoint.cpp",
+        "CryptoType.cpp",
         "Devmapper.cpp",
         "EncryptInplace.cpp",
         "FileDeviceUtils.cpp",
@@ -142,8 +143,8 @@
         "model/ObbVolume.cpp",
         "model/PrivateVolume.cpp",
         "model/PublicVolume.cpp",
-        "model/VolumeBase.cpp",
         "model/StubVolume.cpp",
+        "model/VolumeBase.cpp",
     ],
     product_variables: {
         arc: {
diff --git a/CryptoType.cpp b/CryptoType.cpp
new file mode 100644
index 0000000..155848e
--- /dev/null
+++ b/CryptoType.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2020 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 "CryptoType.h"
+
+#include <string.h>
+
+#include <android-base/logging.h>
+#include <cutils/properties.h>
+
+namespace android {
+namespace vold {
+
+const CryptoType& lookup_crypto_algorithm(const CryptoType table[], int table_len,
+                                          const CryptoType& default_alg, const char* property) {
+    char paramstr[PROPERTY_VALUE_MAX];
+
+    property_get(property, paramstr, default_alg.get_config_name());
+    for (int i = 0; i < table_len; i++) {
+        if (strcmp(paramstr, table[i].get_config_name()) == 0) {
+            return table[i];
+        }
+    }
+    LOG(ERROR) << "Invalid name (" << paramstr << ") for " << property << ".  Defaulting to "
+               << default_alg.get_config_name() << ".";
+    return default_alg;
+}
+
+}  // namespace vold
+}  // namespace android
diff --git a/CryptoType.h b/CryptoType.h
new file mode 100644
index 0000000..7ec419b
--- /dev/null
+++ b/CryptoType.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#pragma once
+
+#include <stdlib.h>
+
+namespace android {
+namespace vold {
+
+// Struct representing an encryption algorithm supported by vold.
+// "config_name" represents the name we give the algorithm in
+// read-only properties and fstab files
+// "kernel_name" is the name we present to the Linux kernel
+// "keysize" is the size of the key in bytes.
+struct CryptoType {
+    // We should only be constructing CryptoTypes as part of
+    // supported_crypto_types[].  We do it via this pseudo-builder pattern,
+    // which isn't pure or fully protected as a concession to being able to
+    // do it all at compile time.  Add new CryptoTypes in
+    // supported_crypto_types[] below.
+    constexpr CryptoType() : CryptoType(nullptr, nullptr, 0xFFFFFFFF) {}
+    constexpr CryptoType set_keysize(size_t size) const {
+        return CryptoType(this->config_name, this->kernel_name, size);
+    }
+    constexpr CryptoType set_config_name(const char* property) const {
+        return CryptoType(property, this->kernel_name, this->keysize);
+    }
+    constexpr CryptoType set_kernel_name(const char* crypto) const {
+        return CryptoType(this->config_name, crypto, this->keysize);
+    }
+
+    constexpr const char* get_config_name() const { return config_name; }
+    constexpr const char* get_kernel_name() const { return kernel_name; }
+    constexpr size_t get_keysize() const { return keysize; }
+
+  private:
+    const char* config_name;
+    const char* kernel_name;
+    size_t keysize;
+
+    constexpr CryptoType(const char* property, const char* crypto, size_t ksize)
+        : config_name(property), kernel_name(crypto), keysize(ksize) {}
+};
+
+// Use the named android property to look up a type from the table
+// If the property is not set or matches no table entry, return the default.
+const CryptoType& lookup_crypto_algorithm(const CryptoType table[], int table_len,
+                                          const CryptoType& default_alg, const char* property);
+
+// Some useful types
+
+constexpr CryptoType invalid_crypto_type = CryptoType();
+
+constexpr CryptoType aes_256_xts = CryptoType()
+                                           .set_config_name("aes-256-xts")
+                                           .set_kernel_name("aes-xts-plain64")
+                                           .set_keysize(64);
+
+constexpr CryptoType adiantum = CryptoType()
+                                        .set_config_name("adiantum")
+                                        .set_kernel_name("xchacha12,aes-adiantum-plain64")
+                                        .set_keysize(32);
+
+// Support compile-time validation of a crypto type table
+
+template <typename T, size_t N>
+constexpr size_t array_length(T (&)[N]) {
+    return N;
+}
+
+constexpr bool isValidCryptoType(size_t max_keylen, const CryptoType& crypto_type) {
+    return ((crypto_type.get_config_name() != nullptr) &&
+            (crypto_type.get_kernel_name() != nullptr) &&
+            (crypto_type.get_keysize() <= max_keylen));
+}
+
+// Confirms that all supported_crypto_types have a small enough keysize and
+// had both set_config_name() and set_kernel_name() called.
+// Note in C++11 that constexpr functions can only have a single line.
+// So our code is a bit convoluted (using recursion instead of a loop),
+// but it's asserting at compile time that all of our key lengths are valid.
+constexpr bool validateSupportedCryptoTypes(size_t max_keylen, const CryptoType types[],
+                                            size_t len) {
+    return len == 0 || (isValidCryptoType(max_keylen, types[len - 1]) &&
+                        validateSupportedCryptoTypes(max_keylen, types, len - 1));
+}
+
+}  // namespace vold
+}  // namespace android
diff --git a/FsCrypt.cpp b/FsCrypt.cpp
index 0619fba..141f4c9 100644
--- a/FsCrypt.cpp
+++ b/FsCrypt.cpp
@@ -65,6 +65,9 @@
 using android::vold::IsFilesystemSupported;
 using android::vold::kEmptyAuthentication;
 using android::vold::KeyBuffer;
+using android::vold::makeGen;
+using android::vold::retrieveKey;
+using android::vold::retrieveOrGenerateKey;
 using android::vold::SetQuotaInherit;
 using android::vold::SetQuotaProjectId;
 using android::vold::writeStringToFile;
@@ -186,7 +189,7 @@
     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)) {
+        if (retrieveKey(ce_key_path, auth, ce_key)) {
             LOG(DEBUG) << "Successfully retrieved key";
             fixate_user_ce_key(directory_path, ce_key_path, paths);
             return true;
@@ -277,8 +280,8 @@
     EncryptionOptions options;
     if (!get_data_file_encryption_options(&options)) return false;
     KeyBuffer de_key, ce_key;
-    if (!generateStorageKey(options, &de_key)) return false;
-    if (!generateStorageKey(options, &ce_key)) return false;
+    if (!generateStorageKey(makeGen(options), &de_key)) return false;
+    if (!generateStorageKey(makeGen(options), &ce_key)) return false;
     if (create_ephemeral) {
         // If the key should be created as ephemeral, don't store it.
         s_ephemeral_users.insert(user_id);
@@ -352,7 +355,7 @@
         if (s_de_policies.count(user_id) == 0) {
             auto key_path = de_dir + "/" + entry->d_name;
             KeyBuffer de_key;
-            if (!android::vold::retrieveKey(key_path, kEmptyAuthentication, &de_key)) return false;
+            if (!retrieveKey(key_path, kEmptyAuthentication, &de_key)) return false;
             EncryptionPolicy de_policy;
             if (!install_storage_key(DATA_MNT_POINT, options, de_key, &de_policy)) return false;
             s_de_policies[user_id] = de_policy;
@@ -375,8 +378,8 @@
     if (!get_data_file_encryption_options(&options)) return false;
 
     KeyBuffer device_key;
-    if (!android::vold::retrieveKey(true, kEmptyAuthentication, device_key_path, device_key_temp,
-                                    options, &device_key))
+    if (!retrieveOrGenerateKey(device_key_path, device_key_temp, kEmptyAuthentication,
+                               makeGen(options), &device_key))
         return false;
 
     EncryptionPolicy device_policy;
@@ -395,7 +398,7 @@
     LOG(INFO) << "Wrote system DE key reference to:" << ref_filename;
 
     KeyBuffer per_boot_key;
-    if (!generateStorageKey(options, &per_boot_key)) return false;
+    if (!generateStorageKey(makeGen(options), &per_boot_key)) return false;
     EncryptionPolicy per_boot_policy;
     if (!install_storage_key(DATA_MNT_POINT, options, per_boot_key, &per_boot_policy)) return false;
     std::string per_boot_ref_filename = std::string("/data") + fscrypt_key_per_boot_ref;
@@ -604,7 +607,7 @@
     EncryptionOptions options;
     if (!get_volume_file_encryption_options(&options)) return false;
     KeyBuffer key;
-    if (!android::vold::retrieveKey(true, auth, key_path, key_path + "_tmp", options, &key))
+    if (!retrieveOrGenerateKey(key_path, key_path + "_tmp", auth, makeGen(options), &key))
         return false;
     if (!install_storage_key(BuildDataPath(volume_uuid), options, key, policy)) return false;
     return true;
@@ -623,12 +626,12 @@
     auto const directory_path = get_ce_key_directory_path(user_id);
     KeyBuffer ce_key;
     std::string ce_key_current_path = get_ce_key_current_path(directory_path);
-    if (android::vold::retrieveKey(ce_key_current_path, retrieve_auth, &ce_key)) {
+    if (retrieveKey(ce_key_current_path, retrieve_auth, &ce_key)) {
         LOG(DEBUG) << "Successfully retrieved key";
         // TODO(147732812): Remove this once Locksettingservice is fixed.
         // Currently it calls fscrypt_clear_user_key_auth with a secret when lockscreen is
         // changed from swipe to none or vice-versa
-    } else if (android::vold::retrieveKey(ce_key_current_path, kEmptyAuthentication, &ce_key)) {
+    } else if (retrieveKey(ce_key_current_path, kEmptyAuthentication, &ce_key)) {
         LOG(DEBUG) << "Successfully retrieved key with empty auth";
     } else {
         LOG(ERROR) << "Failed to retrieve key for user " << user_id;
diff --git a/KeyUtil.cpp b/KeyUtil.cpp
index ae4d70b..2e810ff 100644
--- a/KeyUtil.cpp
+++ b/KeyUtil.cpp
@@ -36,8 +36,20 @@
 namespace android {
 namespace vold {
 
-bool randomKey(KeyBuffer* key) {
-    *key = KeyBuffer(FSCRYPT_MAX_KEY_SIZE);
+const KeyGeneration makeGen(const EncryptionOptions& options) {
+    return KeyGeneration{FSCRYPT_MAX_KEY_SIZE, true, options.use_hw_wrapped_key};
+}
+
+const KeyGeneration makeGen(const CryptoType& crypto) {
+    return KeyGeneration{crypto.get_keysize(), true, false};
+}
+
+const KeyGeneration neverGen() {
+    return KeyGeneration{0, false, false};
+}
+
+static bool randomKey(size_t size, KeyBuffer* key) {
+    *key = KeyBuffer(size);
     if (ReadRandomBytes(key->size(), key->data()) != 0) {
         // TODO status_t plays badly with PLOG, fix it.
         LOG(ERROR) << "Random read failed";
@@ -46,11 +58,17 @@
     return true;
 }
 
-bool generateStorageKey(const EncryptionOptions& options, KeyBuffer* key) {
-    if (options.use_hw_wrapped_key) {
+bool generateStorageKey(const KeyGeneration& gen, KeyBuffer* key) {
+    if (!gen.allow_gen) return false;
+    if (gen.use_hw_wrapped_key) {
+        if (gen.keysize != FSCRYPT_MAX_KEY_SIZE) {
+            LOG(ERROR) << "Cannot generate a wrapped key " << gen.keysize << " bytes long";
+            return false;
+        }
         return generateWrappedStorageKey(key);
+    } else {
+        return randomKey(gen.keysize, key);
     }
-    return randomKey(key);
 }
 
 // Return true if the kernel supports the ioctls to add/remove fscrypt keys
@@ -315,19 +333,19 @@
     return true;
 }
 
-bool retrieveKey(bool create_if_absent, const KeyAuthentication& key_authentication,
-                 const std::string& key_path, const std::string& tmp_path,
-                 const EncryptionOptions& options, KeyBuffer* key, bool keepOld) {
+bool retrieveOrGenerateKey(const std::string& key_path, const std::string& tmp_path,
+                           const KeyAuthentication& key_authentication, const KeyGeneration& gen,
+                           KeyBuffer* key, bool keepOld) {
     if (pathExists(key_path)) {
         LOG(DEBUG) << "Key exists, using: " << key_path;
         if (!retrieveKey(key_path, key_authentication, key, keepOld)) return false;
     } else {
-        if (!create_if_absent) {
+        if (!gen.allow_gen) {
             LOG(ERROR) << "No key found in " << key_path;
             return false;
         }
         LOG(INFO) << "Creating new key in " << key_path;
-        if (!generateStorageKey(options, key)) return false;
+        if (!generateStorageKey(gen, key)) return false;
         if (!storeKeyAtomically(key_path, tmp_path, key_authentication, *key)) return false;
     }
     return true;
diff --git a/KeyUtil.h b/KeyUtil.h
index 878b4ab..16aaf99 100644
--- a/KeyUtil.h
+++ b/KeyUtil.h
@@ -17,6 +17,7 @@
 #ifndef ANDROID_VOLD_KEYUTIL_H
 #define ANDROID_VOLD_KEYUTIL_H
 
+#include "CryptoType.h"
 #include "KeyBuffer.h"
 #include "KeyStorage.h"
 
@@ -30,9 +31,26 @@
 
 using namespace android::fscrypt;
 
-bool randomKey(KeyBuffer* key);
+// Description of how to generate a key when needed.
+struct KeyGeneration {
+    size_t keysize;
+    bool allow_gen;
+    bool use_hw_wrapped_key;
+};
 
-bool generateStorageKey(const EncryptionOptions& options, KeyBuffer* key);
+// Generate a key as specified in KeyGeneration
+bool generateStorageKey(const KeyGeneration& gen, KeyBuffer* key);
+
+// Returns KeyGeneration suitable for key as described in EncryptionOptions
+const KeyGeneration makeGen(const EncryptionOptions& options);
+
+// Returns KeyGeneration suitable for key as described in CryptoType
+const KeyGeneration makeGen(const CryptoType& crypto);
+
+// Returns a key with allow_gen false so generateStorageKey returns false;
+// this is used to indicate to retrieveOrGenerateKey that a key should not
+// be generated.
+const KeyGeneration neverGen();
 
 bool isFsKeyringSupported(void);
 
@@ -58,9 +76,9 @@
 // In the latter case, the caller is responsible for dropping caches.
 bool evictKey(const std::string& mountpoint, const EncryptionPolicy& policy);
 
-bool retrieveKey(bool create_if_absent, const KeyAuthentication& key_authentication,
-                 const std::string& key_path, const std::string& tmp_path,
-                 const EncryptionOptions& options, KeyBuffer* key, bool keepOld = true);
+bool retrieveOrGenerateKey(const std::string& key_path, const std::string& tmp_path,
+                           const KeyAuthentication& key_authentication, const KeyGeneration& gen,
+                           KeyBuffer* key, bool keepOld = true);
 
 }  // namespace vold
 }  // namespace android
diff --git a/MetadataCrypt.cpp b/MetadataCrypt.cpp
index 76ea9eb..938ba34 100644
--- a/MetadataCrypt.cpp
+++ b/MetadataCrypt.cpp
@@ -36,6 +36,7 @@
 #include <libdm/dm.h>
 
 #include "Checkpoint.h"
+#include "CryptoType.h"
 #include "EncryptInplace.h"
 #include "KeyStorage.h"
 #include "KeyUtil.h"
@@ -45,6 +46,9 @@
 
 #define TABLE_LOAD_RETRIES 10
 
+namespace android {
+namespace vold {
+
 using android::fs_mgr::FstabEntry;
 using android::fs_mgr::GetEntryForMountPoint;
 using android::vold::KeyBuffer;
@@ -55,6 +59,21 @@
 static const char* kFn_keymaster_key_blob = "keymaster_key_blob";
 static const char* kFn_keymaster_key_blob_upgraded = "keymaster_key_blob_upgraded";
 
+constexpr CryptoType supported_crypto_types[] = {aes_256_xts, adiantum};
+
+static_assert(validateSupportedCryptoTypes(64, supported_crypto_types,
+                                           array_length(supported_crypto_types)),
+              "We have a CryptoType which was incompletely constructed.");
+
+constexpr CryptoType legacy_aes_256_xts =
+        CryptoType().set_config_name("aes-256-xts").set_kernel_name("AES-256-XTS").set_keysize(64);
+
+constexpr CryptoType legacy_crypto_types[] = {legacy_aes_256_xts};
+
+static_assert(validateSupportedCryptoTypes(64, legacy_crypto_types,
+                                           array_length(legacy_crypto_types)),
+              "We have a CryptoType which was incompletely constructed.");
+
 static bool mount_via_fs_mgr(const char* mount_point, const char* blk_device) {
     // We're about to mount data not verified by verified boot.  Tell Keymaster that early boot has
     // ended.
@@ -85,9 +104,6 @@
     return true;
 }
 
-namespace android {
-namespace vold {
-
 // Note: It is possible to orphan a key if it is removed before deleting
 // Update this once keymaster APIs change, and we have a proper commit.
 static void commit_key(const std::string& dir) {
@@ -113,29 +129,12 @@
     LOG(INFO) << "Old Key deleted: " << dir;
 }
 
-static bool retrieveMetadataKey(bool create_if_absent, const std::string& key_path,
-                                const std::string& tmp_path, KeyBuffer* key, bool keepOld) {
-    if (pathExists(key_path)) {
-        LOG(DEBUG) << "Key exists, using: " << key_path;
-        if (!retrieveKey(key_path, kEmptyAuthentication, key, keepOld)) return false;
-    } else {
-        if (!create_if_absent) {
-            LOG(ERROR) << "No key found in " << key_path;
-            return false;
-        }
-        LOG(INFO) << "Creating new key in " << key_path;
-        if (!randomKey(key)) return false;
-        if (!storeKeyAtomically(key_path, tmp_path, kEmptyAuthentication, *key)) return false;
-    }
-    return true;
-}
-
-static bool read_key(const FstabEntry& data_rec, bool create_if_absent, KeyBuffer* key) {
-    if (data_rec.metadata_key_dir.empty()) {
+static bool read_key(const std::string& metadata_key_dir, const KeyGeneration& gen,
+                     KeyBuffer* key) {
+    if (metadata_key_dir.empty()) {
         LOG(ERROR) << "Failed to get metadata_key_dir";
         return false;
     }
-    std::string metadata_key_dir = data_rec.metadata_key_dir;
     std::string sKey;
     auto dir = metadata_key_dir + "/key";
     LOG(DEBUG) << "metadata_key_dir/key: " << dir;
@@ -153,21 +152,18 @@
     Keymaster keymaster;
     if (pathExists(newKeyPath)) {
         if (!android::base::ReadFileToString(newKeyPath, &sKey))
-            LOG(ERROR) << "Failed to read old key: " << dir;
+            LOG(ERROR) << "Failed to read incomplete key: " << dir;
         else if (!keymaster.deleteKey(sKey))
-            LOG(ERROR) << "Old key deletion failed, continuing anyway: " << dir;
+            LOG(ERROR) << "Incomplete key deletion failed, continuing anyway: " << dir;
         else
             unlink(newKeyPath.c_str());
     }
     bool needs_cp = cp_needsCheckpoint();
-    if (!retrieveMetadataKey(create_if_absent, dir, temp, key, needs_cp)) return false;
+    if (!retrieveOrGenerateKey(dir, temp, kEmptyAuthentication, gen, key, needs_cp)) return false;
     if (needs_cp && pathExists(newKeyPath)) std::thread(commit_key, dir).detach();
     return true;
 }
 
-}  // namespace vold
-}  // namespace android
-
 static bool get_number_of_sectors(const std::string& real_blkdev, uint64_t* nr_sec) {
     if (android::vold::GetBlockDev512Sectors(real_blkdev, nr_sec) != android::OK) {
         PLOG(ERROR) << "Unable to measure size of " << real_blkdev;
@@ -176,32 +172,11 @@
     return true;
 }
 
-static std::string lookup_cipher(const std::string& cipher_name, bool is_legacy) {
-    if (is_legacy) {
-        if (cipher_name.empty() || cipher_name == "aes-256-xts") {
-            return "AES-256-XTS";
-        }
-    } else {
-        if (cipher_name.empty() || cipher_name == "aes-256-xts") {
-            return "aes-xts-plain64";
-        } else if (cipher_name == "adiantum") {
-            return "xchacha12,aes-adiantum-plain64";
-        }
-    }
-    LOG(ERROR) << "No metadata cipher named " << cipher_name << " found, is_legacy=" << is_legacy;
-    return "";
-}
-
-static bool create_crypto_blk_dev(const std::string& dm_name, const FstabEntry* data_rec,
+static bool create_crypto_blk_dev(const std::string& dm_name, const std::string& blk_device,
+                                  bool is_legacy, const std::string& cipher, bool set_dun,
                                   const KeyBuffer& key, std::string* crypto_blkdev) {
     uint64_t nr_sec;
-    if (!get_number_of_sectors(data_rec->blk_device, &nr_sec)) return false;
-
-    bool is_legacy;
-    if (!DmTargetDefaultKey::IsLegacy(&is_legacy)) return false;
-
-    auto cipher = lookup_cipher(data_rec->metadata_cipher, is_legacy);
-    if (cipher.empty()) return false;
+    if (!get_number_of_sectors(blk_device, &nr_sec)) return false;
 
     KeyBuffer hex_key_buffer;
     if (android::vold::StrToHex(key, hex_key_buffer) != android::OK) {
@@ -210,16 +185,9 @@
     }
     std::string hex_key(hex_key_buffer.data(), hex_key_buffer.size());
 
-    // Non-legacy driver always sets DUN
-    bool set_dun = !is_legacy || android::base::GetBoolProperty("ro.crypto.set_dun", false);
-    if (!set_dun && data_rec->fs_mgr_flags.checkpoint_blk) {
-        LOG(ERROR) << "Block checkpoints and metadata encryption require ro.crypto.set_dun option";
-        return false;
-    }
-
     DmTable table;
-    table.Emplace<DmTargetDefaultKey>(0, nr_sec, cipher, hex_key, data_rec->blk_device, 0,
-                                      is_legacy, set_dun);
+    table.Emplace<DmTargetDefaultKey>(0, nr_sec, cipher, hex_key, blk_device, 0, is_legacy,
+                                      set_dun);
 
     auto& dm = DeviceMapper::Instance();
     for (int i = 0;; i++) {
@@ -241,6 +209,27 @@
     return true;
 }
 
+static const CryptoType& lookup_cipher_in_table(const CryptoType table[], int table_len,
+                                                const std::string& cipher_name) {
+    if (cipher_name.empty()) return table[0];
+    for (int i = 0; i < table_len; i++) {
+        if (cipher_name == table[i].get_config_name()) {
+            return table[i];
+        }
+    }
+    return invalid_crypto_type;
+}
+
+static const CryptoType& lookup_cipher(const std::string& cipher_name, bool is_legacy) {
+    if (is_legacy) {
+        return lookup_cipher_in_table(legacy_crypto_types, array_length(legacy_crypto_types),
+                                      cipher_name);
+    } else {
+        return lookup_cipher_in_table(supported_crypto_types, array_length(supported_crypto_types),
+                                      cipher_name);
+    }
+}
+
 bool fscrypt_mount_metadata_encrypted(const std::string& blk_device, const std::string& mount_point,
                                       bool needs_encrypt) {
     LOG(DEBUG) << "fscrypt_mount_metadata_encrypted: " << mount_point << " " << needs_encrypt;
@@ -260,11 +249,32 @@
                    << data_rec->blk_device << " for " << mount_point;
         return false;
     }
+
+    bool is_legacy;
+    if (!DmTargetDefaultKey::IsLegacy(&is_legacy)) return false;
+
+    // Non-legacy driver always sets DUN
+    bool set_dun = !is_legacy || android::base::GetBoolProperty("ro.crypto.set_dun", false);
+    if (!set_dun && data_rec->fs_mgr_flags.checkpoint_blk) {
+        LOG(ERROR) << "Block checkpoints and metadata encryption require ro.crypto.set_dun option";
+        return false;
+    }
+
+    auto cipher = lookup_cipher(data_rec->metadata_cipher, is_legacy);
+    if (cipher.get_kernel_name() == nullptr) {
+        LOG(ERROR) << "No metadata cipher named " << data_rec->metadata_cipher
+                   << " found, is_legacy=" << is_legacy;
+        return false;
+    }
+
+    auto gen = needs_encrypt ? makeGen(cipher) : neverGen();
     KeyBuffer key;
-    if (!read_key(*data_rec, needs_encrypt, &key)) return false;
+    if (!read_key(data_rec->metadata_key_dir, gen, &key)) return false;
 
     std::string crypto_blkdev;
-    if (!create_crypto_blk_dev(kDmNameUserdata, data_rec, key, &crypto_blkdev)) return false;
+    if (!create_crypto_blk_dev(kDmNameUserdata, data_rec->blk_device, is_legacy,
+                               cipher.get_kernel_name(), set_dun, key, &crypto_blkdev))
+        return false;
 
     // FIXME handle the corrupt case
     if (needs_encrypt) {
@@ -289,3 +299,6 @@
     mount_via_fs_mgr(data_rec->mount_point.c_str(), crypto_blkdev.c_str());
     return true;
 }
+
+}  // namespace vold
+}  // namespace android
diff --git a/MetadataCrypt.h b/MetadataCrypt.h
index cd0f5e5..a1ce7d8 100644
--- a/MetadataCrypt.h
+++ b/MetadataCrypt.h
@@ -19,7 +19,12 @@
 
 #include <string>
 
+namespace android {
+namespace vold {
+
 bool fscrypt_mount_metadata_encrypted(const std::string& block_device,
                                       const std::string& mount_point, bool needs_encrypt);
 
+}  // namespace vold
+}  // namespace android
 #endif
diff --git a/Utils.cpp b/Utils.cpp
index 6a67071..84bed34 100644
--- a/Utils.cpp
+++ b/Utils.cpp
@@ -255,7 +255,7 @@
         if (!IsFilesystemSupported("sdcardfs")) {
             gid = AID_MEDIA_RW;
         }
-    } else if (StartsWith(pathFromRoot, kAppMediaDir)) {
+    } else if (StartsWith(pathFromRoot, kAppObbDir)) {
         appDir = kAppObbDir;
         if (!IsFilesystemSupported("sdcardfs")) {
             gid = AID_EXT_OBB_RW;
diff --git a/cryptfs.cpp b/cryptfs.cpp
index def306d..4487d3b 100644
--- a/cryptfs.cpp
+++ b/cryptfs.cpp
@@ -19,6 +19,7 @@
 #include "cryptfs.h"
 
 #include "Checkpoint.h"
+#include "CryptoType.h"
 #include "EncryptInplace.h"
 #include "FsCrypt.h"
 #include "Keymaster.h"
@@ -74,7 +75,9 @@
 using android::base::ParseUint;
 using android::base::StringPrintf;
 using android::fs_mgr::GetEntryForMountPoint;
+using android::vold::CryptoType;
 using android::vold::KeyBuffer;
+using android::vold::KeyGeneration;
 using namespace android::dm;
 using namespace std::chrono_literals;
 
@@ -299,6 +302,32 @@
 static int master_key_saved = 0;
 static struct crypt_persist_data* persist_data = NULL;
 
+constexpr CryptoType aes_128_cbc = CryptoType()
+                                           .set_config_name("AES-128-CBC")
+                                           .set_kernel_name("aes-cbc-essiv:sha256")
+                                           .set_keysize(16);
+
+constexpr CryptoType supported_crypto_types[] = {aes_128_cbc, android::vold::adiantum};
+
+static_assert(validateSupportedCryptoTypes(MAX_KEY_LEN, supported_crypto_types,
+                                           array_length(supported_crypto_types)),
+              "We have a CryptoType with keysize > MAX_KEY_LEN or which was "
+              "incompletely constructed.");
+
+static const CryptoType& get_crypto_type() {
+    // We only want to parse this read-only property once.  But we need to wait
+    // until the system is initialized before we can read it.  So we use a static
+    // scoped within this function to get it only once.
+    static CryptoType crypto_type =
+            lookup_crypto_algorithm(supported_crypto_types, array_length(supported_crypto_types),
+                                    aes_128_cbc, "ro.crypto.fde_algorithm");
+    return crypto_type;
+}
+
+const KeyGeneration cryptfs_get_keygen() {
+    return makeGen(get_crypto_type());
+}
+
 /* Should we use keymaster? */
 static int keymaster_check_compatibility() {
     return keymaster_compatibility_cryptfs_scrypt();
@@ -429,118 +458,6 @@
     return;
 }
 
-namespace {
-
-struct CryptoType;
-
-// Use to get the CryptoType in use on this device.
-const CryptoType& get_crypto_type();
-
-struct CryptoType {
-    // We should only be constructing CryptoTypes as part of
-    // supported_crypto_types[].  We do it via this pseudo-builder pattern,
-    // which isn't pure or fully protected as a concession to being able to
-    // do it all at compile time.  Add new CryptoTypes in
-    // supported_crypto_types[] below.
-    constexpr CryptoType() : CryptoType(nullptr, nullptr, 0xFFFFFFFF) {}
-    constexpr CryptoType set_keysize(uint32_t size) const {
-        return CryptoType(this->property_name, this->crypto_name, size);
-    }
-    constexpr CryptoType set_property_name(const char* property) const {
-        return CryptoType(property, this->crypto_name, this->keysize);
-    }
-    constexpr CryptoType set_crypto_name(const char* crypto) const {
-        return CryptoType(this->property_name, crypto, this->keysize);
-    }
-
-    constexpr const char* get_property_name() const { return property_name; }
-    constexpr const char* get_crypto_name() const { return crypto_name; }
-    constexpr uint32_t get_keysize() const { return keysize; }
-
-  private:
-    const char* property_name;
-    const char* crypto_name;
-    uint32_t keysize;
-
-    constexpr CryptoType(const char* property, const char* crypto, uint32_t ksize)
-        : property_name(property), crypto_name(crypto), keysize(ksize) {}
-    friend const CryptoType& get_crypto_type();
-    static const CryptoType& get_device_crypto_algorithm();
-};
-
-// We only want to parse this read-only property once.  But we need to wait
-// until the system is initialized before we can read it.  So we use a static
-// scoped within this function to get it only once.
-const CryptoType& get_crypto_type() {
-    static CryptoType crypto_type = CryptoType::get_device_crypto_algorithm();
-    return crypto_type;
-}
-
-constexpr CryptoType default_crypto_type = CryptoType()
-                                               .set_property_name("AES-128-CBC")
-                                               .set_crypto_name("aes-cbc-essiv:sha256")
-                                               .set_keysize(16);
-
-constexpr CryptoType supported_crypto_types[] = {
-    default_crypto_type,
-    CryptoType()
-        .set_property_name("adiantum")
-        .set_crypto_name("xchacha12,aes-adiantum-plain64")
-        .set_keysize(32),
-    // Add new CryptoTypes here.  Order is not important.
-};
-
-// ---------- START COMPILE-TIME SANITY CHECK BLOCK -------------------------
-// We confirm all supported_crypto_types have a small enough keysize and
-// had both set_property_name() and set_crypto_name() called.
-
-template <typename T, size_t N>
-constexpr size_t array_length(T (&)[N]) {
-    return N;
-}
-
-constexpr bool indexOutOfBoundsForCryptoTypes(size_t index) {
-    return (index >= array_length(supported_crypto_types));
-}
-
-constexpr bool isValidCryptoType(const CryptoType& crypto_type) {
-    return ((crypto_type.get_property_name() != nullptr) &&
-            (crypto_type.get_crypto_name() != nullptr) &&
-            (crypto_type.get_keysize() <= MAX_KEY_LEN));
-}
-
-// Note in C++11 that constexpr functions can only have a single line.
-// So our code is a bit convoluted (using recursion instead of a loop),
-// but it's asserting at compile time that all of our key lengths are valid.
-constexpr bool validateSupportedCryptoTypes(size_t index) {
-    return indexOutOfBoundsForCryptoTypes(index) ||
-           (isValidCryptoType(supported_crypto_types[index]) &&
-            validateSupportedCryptoTypes(index + 1));
-}
-
-static_assert(validateSupportedCryptoTypes(0),
-              "We have a CryptoType with keysize > MAX_KEY_LEN or which was "
-              "incompletely constructed.");
-//  ---------- END COMPILE-TIME SANITY CHECK BLOCK -------------------------
-
-// Don't call this directly, use get_crypto_type(), which caches this result.
-const CryptoType& CryptoType::get_device_crypto_algorithm() {
-    constexpr char CRYPT_ALGO_PROP[] = "ro.crypto.fde_algorithm";
-    char paramstr[PROPERTY_VALUE_MAX];
-
-    property_get(CRYPT_ALGO_PROP, paramstr, default_crypto_type.get_property_name());
-    for (auto const& ctype : supported_crypto_types) {
-        if (strcmp(paramstr, ctype.get_property_name()) == 0) {
-            return ctype;
-        }
-    }
-    ALOGE("Invalid name (%s) for %s.  Defaulting to %s\n", paramstr, CRYPT_ALGO_PROP,
-          default_crypto_type.get_property_name());
-    return default_crypto_type;
-}
-
-}  // namespace
-
 /**
  * Gets the default device scrypt parameters for key derivation time tuning.
  * The parameters should lead to about one second derivation time for the
@@ -560,14 +477,6 @@
     ftr->p_factor = pf;
 }
 
-uint32_t cryptfs_get_keysize() {
-    return get_crypto_type().get_keysize();
-}
-
-const char* cryptfs_get_crypto_name() {
-    return get_crypto_type().get_crypto_name();
-}
-
 static uint64_t get_fs_size(const char* dev) {
     int fd, block_size;
     struct ext4_super_block sb;
@@ -1929,9 +1838,10 @@
  */
 int cryptfs_setup_ext_volume(const char* label, const char* real_blkdev, const KeyBuffer& key,
                              std::string* out_crypto_blkdev) {
-    if (key.size() != cryptfs_get_keysize()) {
-        SLOGE("Raw keysize %zu does not match crypt keysize %" PRIu32, key.size(),
-              cryptfs_get_keysize());
+    auto crypto_type = get_crypto_type();
+    if (key.size() != crypto_type.get_keysize()) {
+        SLOGE("Raw keysize %zu does not match crypt keysize %zu", key.size(),
+              crypto_type.get_keysize());
         return -1;
     }
     uint64_t nr_sec = 0;
@@ -1943,8 +1853,8 @@
     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 = cryptfs_get_keysize();
-    strlcpy((char*)ext_crypt_ftr.crypto_type_name, cryptfs_get_crypto_name(),
+    ext_crypt_ftr.keysize = crypto_type.get_keysize();
+    strlcpy((char*)ext_crypt_ftr.crypto_type_name, crypto_type.get_kernel_name(),
             MAX_CRYPTO_TYPE_NAME_LEN);
     uint32_t flags = 0;
     if (fscrypt_is_native() &&
@@ -2078,7 +1988,7 @@
 }
 
 /* Initialize a crypt_mnt_ftr structure.  The keysize is
- * defaulted to cryptfs_get_keysize() bytes, and the filesystem size to 0.
+ * defaulted to get_crypto_type().get_keysize() bytes, and the filesystem size to 0.
  * Presumably, at a minimum, the caller will update the
  * filesystem size and crypto_type_name after calling this function.
  */
@@ -2090,7 +2000,7 @@
     ftr->major_version = CURRENT_MAJOR_VERSION;
     ftr->minor_version = CURRENT_MINOR_VERSION;
     ftr->ftr_size = sizeof(struct crypt_mnt_ftr);
-    ftr->keysize = cryptfs_get_keysize();
+    ftr->keysize = get_crypto_type().get_keysize();
 
     switch (keymaster_check_compatibility()) {
         case 1:
@@ -2334,7 +2244,7 @@
             crypt_ftr.flags |= CRYPT_INCONSISTENT_STATE;
         }
         crypt_ftr.crypt_type = crypt_type;
-        strlcpy((char*)crypt_ftr.crypto_type_name, cryptfs_get_crypto_name(),
+        strlcpy((char*)crypt_ftr.crypto_type_name, get_crypto_type().get_kernel_name(),
                 MAX_CRYPTO_TYPE_NAME_LEN);
 
         /* Make an encrypted master key */
diff --git a/cryptfs.h b/cryptfs.h
index 9b5eae7..872806e 100644
--- a/cryptfs.h
+++ b/cryptfs.h
@@ -26,6 +26,7 @@
 #include <cutils/properties.h>
 
 #include "KeyBuffer.h"
+#include "KeyUtil.h"
 
 #define CRYPT_FOOTER_OFFSET 0x4000
 
@@ -73,8 +74,6 @@
 const char* cryptfs_get_password(void);
 void cryptfs_clear_password(void);
 int cryptfs_isConvertibleToFBE(void);
-
-uint32_t cryptfs_get_keysize();
-const char* cryptfs_get_crypto_name();
+const android::vold::KeyGeneration cryptfs_get_keygen();
 
 #endif /* ANDROID_VOLD_CRYPTFS_H */
diff --git a/model/Disk.cpp b/model/Disk.cpp
index 7ecc89e..b2ecc50 100644
--- a/model/Disk.cpp
+++ b/model/Disk.cpp
@@ -16,6 +16,7 @@
 
 #include "Disk.h"
 #include "FsCrypt.h"
+#include "KeyUtil.h"
 #include "PrivateVolume.h"
 #include "PublicVolume.h"
 #include "Utils.h"
@@ -535,11 +536,12 @@
         return -EIO;
     }
 
-    std::string keyRaw;
-    if (ReadRandomBytes(cryptfs_get_keysize(), keyRaw) != OK) {
+    KeyBuffer key;
+    if (!generateStorageKey(cryptfs_get_keygen(), &key)) {
         LOG(ERROR) << "Failed to generate key";
         return -EIO;
     }
+    std::string keyRaw(key.begin(), key.end());
 
     std::string partGuid;
     StrToHex(partGuidRaw, partGuid);