Merge changes from topics "metadata_wrapped_key_aosp", "volume_metadata"

* changes:
  On newer devices, use dm-default-key to encrypt SD cards
  vold: Wrapped key support for metadata encryption
  Refactor: make makeGen local
diff --git a/Android.bp b/Android.bp
index dae0859..9d87f68 100644
--- a/Android.bp
+++ b/Android.bp
@@ -144,6 +144,7 @@
         "model/PublicVolume.cpp",
         "model/StubVolume.cpp",
         "model/VolumeBase.cpp",
+        "model/VolumeEncryption.cpp",
     ],
     product_variables: {
         arc: {
diff --git a/FsCrypt.cpp b/FsCrypt.cpp
index c0ec3eb..a01ed5e 100644
--- a/FsCrypt.cpp
+++ b/FsCrypt.cpp
@@ -64,7 +64,7 @@
 using android::vold::BuildDataPath;
 using android::vold::kEmptyAuthentication;
 using android::vold::KeyBuffer;
-using android::vold::makeGen;
+using android::vold::KeyGeneration;
 using android::vold::retrieveKey;
 using android::vold::retrieveOrGenerateKey;
 using android::vold::writeStringToFile;
@@ -94,6 +94,11 @@
 
 }  // namespace
 
+// Returns KeyGeneration suitable for key as described in EncryptionOptions
+static KeyGeneration makeGen(const EncryptionOptions& options) {
+    return KeyGeneration{FSCRYPT_MAX_KEY_SIZE, true, options.use_hw_wrapped_key};
+}
+
 static bool fscrypt_is_emulated() {
     return property_get_bool("persist.sys.emulate_fbe", false);
 }
diff --git a/KeyUtil.cpp b/KeyUtil.cpp
index 2e810ff..6200c42 100644
--- a/KeyUtil.cpp
+++ b/KeyUtil.cpp
@@ -36,14 +36,6 @@
 namespace android {
 namespace vold {
 
-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};
 }
diff --git a/KeyUtil.h b/KeyUtil.h
index 16aaf99..dcb1dc7 100644
--- a/KeyUtil.h
+++ b/KeyUtil.h
@@ -17,7 +17,6 @@
 #ifndef ANDROID_VOLD_KEYUTIL_H
 #define ANDROID_VOLD_KEYUTIL_H
 
-#include "CryptoType.h"
 #include "KeyBuffer.h"
 #include "KeyStorage.h"
 
@@ -41,12 +40,6 @@
 // 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.
diff --git a/MetadataCrypt.cpp b/MetadataCrypt.cpp
index 938ba34..7891bee 100644
--- a/MetadataCrypt.cpp
+++ b/MetadataCrypt.cpp
@@ -30,6 +30,7 @@
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/properties.h>
+#include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <cutils/fs.h>
 #include <fs_mgr.h>
@@ -54,11 +55,20 @@
 using android::vold::KeyBuffer;
 using namespace android::dm;
 
+// Parsed from metadata options
+struct CryptoOptions {
+    struct CryptoType cipher = invalid_crypto_type;
+    bool is_legacy = false;
+    bool set_dun = true;  // Non-legacy driver always sets DUN
+    bool use_hw_wrapped_key = false;
+};
+
 static const std::string kDmNameUserdata = "userdata";
 
 static const char* kFn_keymaster_key_blob = "keymaster_key_blob";
 static const char* kFn_keymaster_key_blob_upgraded = "keymaster_key_blob_upgraded";
 
+// The first entry in this table is the default crypto type.
 constexpr CryptoType supported_crypto_types[] = {aes_256_xts, adiantum};
 
 static_assert(validateSupportedCryptoTypes(64, supported_crypto_types,
@@ -68,12 +78,14 @@
 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)),
+static_assert(isValidCryptoType(64, legacy_aes_256_xts),
               "We have a CryptoType which was incompletely constructed.");
 
+// Returns KeyGeneration suitable for key as described in CryptoOptions
+const KeyGeneration makeGen(const CryptoOptions& options) {
+    return KeyGeneration{options.cipher.get_keysize(), true, options.use_hw_wrapped_key};
+}
+
 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.
@@ -173,21 +185,38 @@
 }
 
 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(blk_device, &nr_sec)) return false;
+                                  const KeyBuffer& key, const CryptoOptions& options,
+                                  std::string* crypto_blkdev, uint64_t* nr_sec) {
+    if (!get_number_of_sectors(blk_device, nr_sec)) return false;
+    // TODO(paulcrowley): don't hardcode that DmTargetDefaultKey uses 4096-byte
+    // sectors
+    *nr_sec &= ~7;
+
+    KeyBuffer module_key;
+    if (options.use_hw_wrapped_key) {
+        if (!exportWrappedStorageKey(key, &module_key)) {
+            LOG(ERROR) << "Failed to get ephemeral wrapped key";
+            return false;
+        }
+    } else {
+        module_key = key;
+    }
 
     KeyBuffer hex_key_buffer;
-    if (android::vold::StrToHex(key, hex_key_buffer) != android::OK) {
+    if (android::vold::StrToHex(module_key, hex_key_buffer) != android::OK) {
         LOG(ERROR) << "Failed to turn key to hex";
         return false;
     }
     std::string hex_key(hex_key_buffer.data(), hex_key_buffer.size());
 
+    auto target = std::make_unique<DmTargetDefaultKey>(0, *nr_sec, options.cipher.get_kernel_name(),
+                                                       hex_key, blk_device, 0);
+    if (options.is_legacy) target->SetIsLegacy();
+    if (options.set_dun) target->SetSetDun();
+    if (options.use_hw_wrapped_key) target->SetWrappedKeyV0();
+
     DmTable table;
-    table.Emplace<DmTargetDefaultKey>(0, nr_sec, cipher, hex_key, blk_device, 0, is_legacy,
-                                      set_dun);
+    table.AddTarget(std::move(target));
 
     auto& dm = DeviceMapper::Instance();
     for (int i = 0;; i++) {
@@ -209,25 +238,38 @@
     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];
+static const CryptoType& lookup_cipher(const std::string& cipher_name) {
+    if (cipher_name.empty()) return supported_crypto_types[0];
+    for (size_t i = 0; i < array_length(supported_crypto_types); i++) {
+        if (cipher_name == supported_crypto_types[i].get_config_name()) {
+            return supported_crypto_types[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);
+static bool parse_options(const std::string& options_string, CryptoOptions* options) {
+    auto parts = android::base::Split(options_string, ":");
+    if (parts.size() < 1 || parts.size() > 2) {
+        LOG(ERROR) << "Invalid metadata encryption option: " << options_string;
+        return false;
     }
+    std::string cipher_name = parts[0];
+    options->cipher = lookup_cipher(cipher_name);
+    if (options->cipher.get_kernel_name() == nullptr) {
+        LOG(ERROR) << "No metadata cipher named " << cipher_name << " found";
+        return false;
+    }
+
+    if (parts.size() == 2) {
+        if (parts[1] == "wrappedkey_v0") {
+            options->use_hw_wrapped_key = true;
+        } else {
+            LOG(ERROR) << "Invalid metadata encryption flag: " << parts[1];
+            return false;
+        }
+    }
+    return true;
 }
 
 bool fscrypt_mount_metadata_encrypted(const std::string& blk_device, const std::string& mount_point,
@@ -253,33 +295,36 @@
     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;
+    CryptoOptions options;
+    if (is_legacy) {
+        if (!data_rec->metadata_encryption.empty()) {
+            LOG(ERROR) << "metadata_encryption options cannot be set in legacy mode";
+            return false;
+        }
+        options.cipher = legacy_aes_256_xts;
+        options.is_legacy = true;
+        options.set_dun = android::base::GetBoolProperty("ro.crypto.set_dun", false);
+        if (!options.set_dun && data_rec->fs_mgr_flags.checkpoint_blk) {
+            LOG(ERROR)
+                    << "Block checkpoints and metadata encryption require ro.crypto.set_dun option";
+            return false;
+        }
+    } else {
+        if (!parse_options(data_rec->metadata_encryption, &options)) 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();
+    auto gen = needs_encrypt ? makeGen(options) : neverGen();
     KeyBuffer key;
     if (!read_key(data_rec->metadata_key_dir, gen, &key)) return false;
 
     std::string crypto_blkdev;
-    if (!create_crypto_blk_dev(kDmNameUserdata, data_rec->blk_device, is_legacy,
-                               cipher.get_kernel_name(), set_dun, key, &crypto_blkdev))
+    uint64_t nr_sec;
+    if (!create_crypto_blk_dev(kDmNameUserdata, data_rec->blk_device, key, options, &crypto_blkdev,
+                               &nr_sec))
         return false;
 
     // FIXME handle the corrupt case
     if (needs_encrypt) {
-        uint64_t nr_sec;
-        if (!get_number_of_sectors(data_rec->blk_device, &nr_sec)) return false;
         LOG(INFO) << "Beginning inplace encryption, nr_sec: " << nr_sec;
         off64_t size_already_done = 0;
         auto rc = cryptfs_enable_inplace(crypto_blkdev.data(), blk_device.data(), nr_sec,
@@ -300,5 +345,27 @@
     return true;
 }
 
+static bool get_volume_options(CryptoOptions* options) {
+    return parse_options(android::base::GetProperty("ro.crypto.volume.metadata.encryption", ""),
+                         options);
+}
+
+bool defaultkey_volume_keygen(KeyGeneration* gen) {
+    CryptoOptions options;
+    if (!get_volume_options(&options)) return false;
+    *gen = makeGen(options);
+    return true;
+}
+
+bool defaultkey_setup_ext_volume(const std::string& label, const std::string& blk_device,
+                                 const KeyBuffer& key, std::string* out_crypto_blkdev) {
+    LOG(DEBUG) << "defaultkey_setup_ext_volume: " << label << " " << blk_device;
+
+    CryptoOptions options;
+    if (!get_volume_options(&options)) return false;
+    uint64_t nr_sec;
+    return create_crypto_blk_dev(label, blk_device, key, options, out_crypto_blkdev, &nr_sec);
+}
+
 }  // namespace vold
 }  // namespace android
diff --git a/MetadataCrypt.h b/MetadataCrypt.h
index a1ce7d8..dc68e7c 100644
--- a/MetadataCrypt.h
+++ b/MetadataCrypt.h
@@ -19,12 +19,21 @@
 
 #include <string>
 
+#include "KeyBuffer.h"
+#include "KeyUtil.h"
+
 namespace android {
 namespace vold {
 
 bool fscrypt_mount_metadata_encrypted(const std::string& block_device,
                                       const std::string& mount_point, bool needs_encrypt);
 
+bool defaultkey_volume_keygen(KeyGeneration* gen);
+
+bool defaultkey_setup_ext_volume(const std::string& label, const std::string& blk_device,
+                                 const android::vold::KeyBuffer& key,
+                                 std::string* out_crypto_blkdev);
+
 }  // namespace vold
 }  // namespace android
 #endif
diff --git a/cryptfs.cpp b/cryptfs.cpp
index 04497b0..da748fd 100644
--- a/cryptfs.cpp
+++ b/cryptfs.cpp
@@ -322,7 +322,7 @@
 }
 
 const KeyGeneration cryptfs_get_keygen() {
-    return makeGen(get_crypto_type());
+    return KeyGeneration{get_crypto_type().get_keysize(), true, false};
 }
 
 /* Should we use keymaster? */
diff --git a/model/Disk.cpp b/model/Disk.cpp
index f92435d..6a6585e 100644
--- a/model/Disk.cpp
+++ b/model/Disk.cpp
@@ -16,11 +16,11 @@
 
 #include "Disk.h"
 #include "FsCrypt.h"
-#include "KeyUtil.h"
 #include "PrivateVolume.h"
 #include "PublicVolume.h"
 #include "Utils.h"
 #include "VolumeBase.h"
+#include "VolumeEncryption.h"
 #include "VolumeManager.h"
 
 #include <android-base/file.h>
@@ -31,8 +31,6 @@
 #include <android-base/strings.h>
 #include <fscrypt/fscrypt.h>
 
-#include "cryptfs.h"
-
 #include <fcntl.h>
 #include <inttypes.h>
 #include <stdio.h>
@@ -507,7 +505,7 @@
     }
 
     KeyBuffer key;
-    if (!generateStorageKey(cryptfs_get_keygen(), &key)) {
+    if (!generate_volume_key(&key)) {
         LOG(ERROR) << "Failed to generate key";
         return -EIO;
     }
diff --git a/model/PrivateVolume.cpp b/model/PrivateVolume.cpp
index 4a0b250..fd3daea 100644
--- a/model/PrivateVolume.cpp
+++ b/model/PrivateVolume.cpp
@@ -17,8 +17,8 @@
 #include "PrivateVolume.h"
 #include "EmulatedVolume.h"
 #include "Utils.h"
+#include "VolumeEncryption.h"
 #include "VolumeManager.h"
-#include "cryptfs.h"
 #include "fs/Ext4.h"
 #include "fs/F2fs.h"
 
@@ -75,9 +75,8 @@
 
     // TODO: figure out better SELinux labels for private volumes
 
-    int res = cryptfs_setup_ext_volume(getId().c_str(), mRawDevPath.c_str(), mKeyRaw, &mDmDevPath);
-    if (res != 0) {
-        PLOG(ERROR) << getId() << " failed to setup cryptfs";
+    if (!setup_ext_volume(getId(), mRawDevPath, mKeyRaw, &mDmDevPath)) {
+        LOG(ERROR) << getId() << " failed to setup metadata encryption";
         return -EIO;
     }
 
diff --git a/model/VolumeEncryption.cpp b/model/VolumeEncryption.cpp
new file mode 100644
index 0000000..5b0e73d
--- /dev/null
+++ b/model/VolumeEncryption.cpp
@@ -0,0 +1,94 @@
+/*
+ * 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 "VolumeEncryption.h"
+
+#include <string>
+
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+
+#include "KeyBuffer.h"
+#include "KeyUtil.h"
+#include "MetadataCrypt.h"
+#include "cryptfs.h"
+
+namespace android {
+namespace vold {
+
+enum class VolumeMethod { kFailed, kCrypt, kDefaultKey };
+
+static VolumeMethod lookup_volume_method() {
+    constexpr uint64_t pre_gki_level = 29;
+    auto first_api_level =
+            android::base::GetUintProperty<uint64_t>("ro.product.first_api_level", 0);
+    auto method = android::base::GetProperty("ro.crypto.volume.metadata.method", "default");
+    if (method == "default") {
+        return first_api_level > pre_gki_level ? VolumeMethod::kDefaultKey : VolumeMethod::kCrypt;
+    } else if (method == "dm-default-key") {
+        return VolumeMethod::kDefaultKey;
+    } else if (method == "dm-crypt") {
+        if (first_api_level > pre_gki_level) {
+            LOG(ERROR) << "volume encryption method dm-crypt cannot be used, "
+                          "ro.product.first_api_level = "
+                       << first_api_level;
+            return VolumeMethod::kFailed;
+        }
+        return VolumeMethod::kCrypt;
+    } else {
+        LOG(ERROR) << "Unknown volume encryption method: " << method;
+        return VolumeMethod::kFailed;
+    }
+}
+
+static VolumeMethod volume_method() {
+    static VolumeMethod method = lookup_volume_method();
+    return method;
+}
+
+bool generate_volume_key(android::vold::KeyBuffer* key) {
+    KeyGeneration gen;
+    switch (volume_method()) {
+        case VolumeMethod::kFailed:
+            LOG(ERROR) << "Volume encryption setup failed";
+            return false;
+        case VolumeMethod::kCrypt:
+            gen = cryptfs_get_keygen();
+            break;
+        case VolumeMethod::kDefaultKey:
+            if (!defaultkey_volume_keygen(&gen)) return false;
+            break;
+    }
+    if (!generateStorageKey(gen, key)) return false;
+    return true;
+}
+
+bool setup_ext_volume(const std::string& label, const std::string& blk_device,
+                      const android::vold::KeyBuffer& key, std::string* out_crypto_blkdev) {
+    switch (volume_method()) {
+        case VolumeMethod::kFailed:
+            LOG(ERROR) << "Volume encryption setup failed";
+            return false;
+        case VolumeMethod::kCrypt:
+            return cryptfs_setup_ext_volume(label.c_str(), blk_device.c_str(), key,
+                                            out_crypto_blkdev) == 0;
+        case VolumeMethod::kDefaultKey:
+            return defaultkey_setup_ext_volume(label, blk_device, key, out_crypto_blkdev);
+    }
+}
+
+}  // namespace vold
+}  // namespace android
diff --git a/model/VolumeEncryption.h b/model/VolumeEncryption.h
new file mode 100644
index 0000000..d06c12b
--- /dev/null
+++ b/model/VolumeEncryption.h
@@ -0,0 +1,32 @@
+/*
+ * 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 <string>
+
+#include "KeyBuffer.h"
+
+namespace android {
+namespace vold {
+
+bool generate_volume_key(android::vold::KeyBuffer* key);
+
+bool setup_ext_volume(const std::string& label, const std::string& blk_device,
+                      const android::vold::KeyBuffer& key, std::string* out_crypto_blkdev);
+
+}  // namespace vold
+}  // namespace android