vold: Wrapped key support for metadata encryption

metadata_encryption fstab option provides details on the cipher
and flags used for metadata encryption. wrappedkey_v0 is provided
to dm-default-key dm device when a wrapped key is used. The
inline encryption hardware unwraps the key and derives the
encryption key used to encrypt metadata without returning the key
in the clear to software.

Bug: 147733587
Test: FBE with metadata encryption using wrapped keys.
Change-Id: Ibf69bdc12bb18d2f0aef8208e65f3a8dececfd2a
diff --git a/MetadataCrypt.cpp b/MetadataCrypt.cpp
index 19a90ad..29ce7c4 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>
@@ -59,6 +60,7 @@
     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";
@@ -81,7 +83,7 @@
 
 // Returns KeyGeneration suitable for key as described in CryptoOptions
 const KeyGeneration makeGen(const CryptoOptions& options) {
-    return KeyGeneration{options.cipher.get_keysize(), true, false};
+    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) {
@@ -188,16 +190,31 @@
     uint64_t nr_sec;
     if (!get_number_of_sectors(blk_device, &nr_sec)) return false;
 
+    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, options.cipher.get_kernel_name(), hex_key,
-                                      blk_device, 0, options.is_legacy, options.set_dun);
+    table.AddTarget(std::move(target));
 
     auto& dm = DeviceMapper::Instance();
     for (int i = 0;; i++) {
@@ -230,11 +247,26 @@
 }
 
 static bool parse_options(const std::string& options_string, CryptoOptions* options) {
-    options->cipher = lookup_cipher(options_string);
-    if (options->cipher.get_kernel_name() == nullptr) {
-        LOG(ERROR) << "No metadata cipher named " << options_string << " found";
+    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;
 }
 
@@ -263,8 +295,8 @@
 
     CryptoOptions options;
     if (is_legacy) {
-        if (!data_rec->metadata_cipher.empty()) {
-            LOG(ERROR) << "metadata_cipher options cannot be set in legacy mode";
+        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;
@@ -276,7 +308,7 @@
             return false;
         }
     } else {
-        if (!parse_options(data_rec->metadata_cipher, &options)) return false;
+        if (!parse_options(data_rec->metadata_encryption, &options)) return false;
     }
 
     auto gen = needs_encrypt ? makeGen(options) : neverGen();