vold: support v2 encryption policies

Add support for setting v2 encryption policies when configured in the
fstab (for internal storage) or in system properties (for adoptable
storage), and for installing and evicting the keys for such policies.

v2 policies support the same encryption modes and flags as v1 policies,
but internally they use a more standard, secure, and flexible KDF.  Due
to this, some future features will be supported by v2 policies only.

Bug: 140500999
Test: Configured a device to use v2 encryption policies (applied the
      needed kernel patches and added
      "fileencryption=aes-256-xts:aes-256-cts:v2" to fstab, and set the
      corresponding system properties for adoptable storage).  Wiped
      userdata, booted device and checked logs to verify that v2
      policies were being used.

      Also enabled virtual SD card and formatted as adoptable storage;
      verified it works and that v2 policies were being used on it.

      Also created, started, and stopped a 2nd user and verified their
      keys were evicted.

      Also verified that the device comes up again after rebooting.

      Also verified that a device using v1 encryption policies continues
      to work, both with and without an updated kernel -- including
      stopping a user so that their keys get evicted.

Change-Id: If64028d8580584b2c33c614cabd5d6b93657f608
diff --git a/KeyUtil.cpp b/KeyUtil.cpp
index c1a82fb..f822377 100644
--- a/KeyUtil.cpp
+++ b/KeyUtil.cpp
@@ -163,28 +163,80 @@
     return true;
 }
 
+// Build a struct fscrypt_key_specifier for use in the key management ioctls.
+static bool buildKeySpecifier(fscrypt_key_specifier* spec, const std::string& raw_ref,
+                              int policy_version) {
+    switch (policy_version) {
+        case 1:
+            if (raw_ref.size() != FSCRYPT_KEY_DESCRIPTOR_SIZE) {
+                LOG(ERROR) << "Invalid key specifier size for v1 encryption policy: "
+                           << raw_ref.size();
+                return false;
+            }
+            spec->type = FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR;
+            memcpy(spec->u.descriptor, raw_ref.c_str(), FSCRYPT_KEY_DESCRIPTOR_SIZE);
+            return true;
+        case 2:
+            if (raw_ref.size() != FSCRYPT_KEY_IDENTIFIER_SIZE) {
+                LOG(ERROR) << "Invalid key specifier size for v2 encryption policy: "
+                           << raw_ref.size();
+                return false;
+            }
+            spec->type = FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER;
+            memcpy(spec->u.identifier, raw_ref.c_str(), FSCRYPT_KEY_IDENTIFIER_SIZE);
+            return true;
+        default:
+            LOG(ERROR) << "Invalid encryption policy version: " << policy_version;
+            return false;
+    }
+}
+
 // Install a file-based encryption key to the kernel, for use by encrypted files
-// on the specified filesystem.
+// on the specified filesystem using the specified encryption policy version.
 //
-// We use FS_IOC_ADD_ENCRYPTION_KEY if the kernel supports it.  Otherwise we add
-// the key to the legacy global session keyring.
+// For v1 policies, we use FS_IOC_ADD_ENCRYPTION_KEY if the kernel supports it.
+// Otherwise we add the key to the legacy global session keyring.
+//
+// For v2 policies, we always use FS_IOC_ADD_ENCRYPTION_KEY; it's the only way
+// the kernel supports.
 //
 // Returns %true on success, %false on failure.  On success also sets *raw_ref
 // to the raw key reference for use in the encryption policy.
-bool installKey(const KeyBuffer& key, const std::string& mountpoint, std::string* raw_ref) {
-    *raw_ref = generateKeyRef((const uint8_t*)key.data(), key.size());
-    if (!isFsKeyringSupported()) {
-        return installKeyLegacy(key, *raw_ref);
-    }
-
+bool installKey(const KeyBuffer& key, const std::string& mountpoint, int policy_version,
+                std::string* raw_ref) {
     // Put the fscrypt_add_key_arg in an automatically-zeroing buffer, since we
     // have to copy the raw key into it.
     KeyBuffer arg_buf(sizeof(struct fscrypt_add_key_arg) + key.size(), 0);
     struct fscrypt_add_key_arg* arg = (struct fscrypt_add_key_arg*)arg_buf.data();
 
-    arg->key_spec.type = FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR;
-    memcpy(arg->key_spec.u.descriptor, raw_ref.c_str(), FSCRYPT_KEY_DESCRIPTOR_SIZE);
+    // Initialize the "key specifier", which is like a name for the key.
+    switch (policy_version) {
+        case 1:
+            // A key for a v1 policy is specified by an arbitrary 8-byte
+            // "descriptor", which must be provided by userspace.  We use the
+            // first 8 bytes from the double SHA-512 of the key itself.
+            *raw_ref = generateKeyRef((const uint8_t*)key.data(), key.size());
+            if (!isFsKeyringSupported()) {
+                return installKeyLegacy(key, *raw_ref);
+            }
+            if (!buildKeySpecifier(&arg->key_spec, *raw_ref, policy_version)) {
+                return false;
+            }
+            break;
+        case 2:
+            // A key for a v2 policy is specified by an 16-byte "identifier",
+            // which is a cryptographic hash of the key itself which the kernel
+            // computes and returns.  Any user-provided value is ignored; we
+            // just need to set the specifier type to indicate that we're adding
+            // this type of key.
+            arg->key_spec.type = FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER;
+            break;
+        default:
+            LOG(ERROR) << "Invalid encryption policy version: " << policy_version;
+            return false;
+    }
 
+    // Provide the raw key.
     arg->raw_size = key.size();
     memcpy(arg->raw, key.data(), key.size());
 
@@ -195,11 +247,14 @@
     }
 
     if (ioctl(fd, FS_IOC_ADD_ENCRYPTION_KEY, arg) != 0) {
-        PLOG(ERROR) << "Failed to install fscrypt key with ref " << keyrefstring(*raw_ref) << " to "
-                    << mountpoint;
+        PLOG(ERROR) << "Failed to install fscrypt key to " << mountpoint;
         return false;
     }
 
+    if (arg->key_spec.type == FSCRYPT_KEY_SPEC_TYPE_IDENTIFIER) {
+        // Retrieve the key identifier that the kernel computed.
+        *raw_ref = std::string((char*)arg->key_spec.u.identifier, FSCRYPT_KEY_IDENTIFIER_SIZE);
+    }
     LOG(DEBUG) << "Installed fscrypt key with ref " << keyrefstring(*raw_ref) << " to "
                << mountpoint;
     return true;
@@ -234,8 +289,8 @@
 // remove the key from the legacy global session keyring.
 //
 // In the latter case, the caller is responsible for dropping caches.
-bool evictKey(const std::string& mountpoint, const std::string& raw_ref) {
-    if (!isFsKeyringSupported()) {
+bool evictKey(const std::string& mountpoint, const std::string& raw_ref, int policy_version) {
+    if (policy_version == 1 && !isFsKeyringSupported()) {
         return evictKeyLegacy(raw_ref);
     }
 
@@ -248,8 +303,9 @@
     struct fscrypt_remove_key_arg arg;
     memset(&arg, 0, sizeof(arg));
 
-    arg.key_spec.type = FSCRYPT_KEY_SPEC_TYPE_DESCRIPTOR;
-    memcpy(arg.key_spec.u.descriptor, raw_ref.c_str(), FSCRYPT_KEY_DESCRIPTOR_SIZE);
+    if (!buildKeySpecifier(&arg.key_spec, raw_ref, policy_version)) {
+        return false;
+    }
 
     std::string ref = keyrefstring(raw_ref);
 
@@ -271,7 +327,8 @@
 
 bool retrieveAndInstallKey(bool create_if_absent, const KeyAuthentication& key_authentication,
                            const std::string& key_path, const std::string& tmp_path,
-                           const std::string& volume_uuid, std::string* key_ref) {
+                           const std::string& volume_uuid, int policy_version,
+                           std::string* key_ref) {
     KeyBuffer key;
     if (pathExists(key_path)) {
         LOG(DEBUG) << "Key exists, using: " << key_path;
@@ -286,7 +343,7 @@
         if (!storeKeyAtomically(key_path, tmp_path, key_authentication, key)) return false;
     }
 
-    if (!installKey(key, BuildDataPath(volume_uuid), key_ref)) {
+    if (!installKey(key, BuildDataPath(volume_uuid), policy_version, key_ref)) {
         LOG(ERROR) << "Failed to install key in " << key_path;
         return false;
     }