Generalize CryptoType infrastructure

More consistency between MetadataCrypt and cryptfs, and steps towards
supporting Adiantum properly in MetadataCrypt.

Test: create private volume on Cuttlefish
Bug: 147814592
Change-Id: Ic3993c1fde11b4f5a9e6cc8ee588a7d92241c6ab
diff --git a/cryptfs.cpp b/cryptfs.cpp
index c3d5f78..38661f3 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"
@@ -71,6 +72,7 @@
 using android::base::ParseUint;
 using android::base::StringPrintf;
 using android::fs_mgr::GetEntryForMountPoint;
+using android::vold::CryptoType;
 using android::vold::KeyBuffer;
 using namespace android::dm;
 using namespace std::chrono_literals;
@@ -296,6 +298,28 @@
 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;
+}
+
 /* Should we use keymaster? */
 static int keymaster_check_compatibility() {
     return keymaster_compatibility_cryptfs_scrypt();
@@ -426,118 +450,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(size_t keysize) const {
-        return CryptoType(this->config_name, this->kernel_name, keysize);
-    }
-    constexpr CryptoType set_config_name(const char* config_name) const {
-        return CryptoType(config_name, this->kernel_name, this->keysize);
-    }
-    constexpr CryptoType set_kernel_name(const char* kernel_name) const {
-        return CryptoType(this->config_name, kernel_name, 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* config, const char* crypto, size_t ksize)
-        : config_name(config), kernel_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_config_name("AES-128-CBC")
-                                                   .set_kernel_name("aes-cbc-essiv:sha256")
-                                                   .set_keysize(16);
-
-constexpr CryptoType supported_crypto_types[] = {
-        default_crypto_type,
-        CryptoType()
-                .set_config_name("adiantum")
-                .set_kernel_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_config_name() and set_kernel_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_config_name() != nullptr) &&
-            (crypto_type.get_kernel_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_config_name());
-    for (auto const& ctype : supported_crypto_types) {
-        if (strcmp(paramstr, ctype.get_config_name()) == 0) {
-            return ctype;
-        }
-    }
-    ALOGE("Invalid name (%s) for %s.  Defaulting to %s\n", paramstr, CRYPT_ALGO_PROP,
-          default_crypto_type.get_config_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
@@ -561,10 +473,6 @@
     return get_crypto_type().get_keysize();
 }
 
-const char* cryptfs_get_kernel_name() {
-    return get_crypto_type().get_kernel_name();
-}
-
 static uint64_t get_fs_size(const char* dev) {
     int fd, block_size;
     struct ext4_super_block sb;
@@ -1913,9 +1821,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()) {
+    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(),
-              cryptfs_get_keysize());
+              crypto_type.get_keysize());
         return -1;
     }
     uint64_t nr_sec = 0;
@@ -1927,8 +1836,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_kernel_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() &&
@@ -2062,7 +1971,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.
  */
@@ -2074,7 +1983,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:
@@ -2318,7 +2227,7 @@
             crypt_ftr.flags |= CRYPT_INCONSISTENT_STATE;
         }
         crypt_ftr.crypt_type = crypt_type;
-        strlcpy((char*)crypt_ftr.crypto_type_name, cryptfs_get_kernel_name(),
+        strlcpy((char*)crypt_ftr.crypto_type_name, get_crypto_type().get_kernel_name(),
                 MAX_CRYPTO_TYPE_NAME_LEN);
 
         /* Make an encrypted master key */