[tee_manager] Support new fuchsia.tee protocols

This change accomplishes the following:
  * Adds support for board-specific configuration files to specify which
    trusted application UUIDs are available on a given environment.
  * Passes through the new `fuchsia.tee.DeviceInfo`.
  * Passes through a config-determined number of different
    `fuchsia.tee.Application` protocols, based on the board config.

Bug: 44664

Test: fx test optee_smoke_test tee-manager-tests

Change-Id: I7e1ca15a4a2799cce9e198cf6be28d5ab501d904
Reviewed-on: https://fuchsia-review.googlesource.com/c/fuchsia/+/364594
Reviewed-by: Chris Tam <godtamit@google.com>
Commit-Queue: RJ Ascani <rjascani@google.com>
Testability-Review: Austin Foxley <afoxley@google.com>
diff --git a/src/security/tee_manager/BUILD.gn b/src/security/tee_manager/BUILD.gn
index ad3a6f0..276711e 100644
--- a/src/security/tee_manager/BUILD.gn
+++ b/src/security/tee_manager/BUILD.gn
@@ -54,7 +54,11 @@
     "//src/lib/zircon/rust:fuchsia-zircon",
     "//third_party/rust_crates:anyhow",
     "//third_party/rust_crates:futures",
+    "//third_party/rust_crates:serde",
+    "//third_party/rust_crates:serde_derive",
+    "//third_party/rust_crates:serde_json",
     "//third_party/rust_crates:thiserror",
+    "//third_party/rust_crates:uuid",
   ]
 
   test_deps = [
@@ -74,6 +78,7 @@
   ]
 }
 
+# TODO(44664): Remove this config when transition to board-specific configs is complete.
 config_data("config") {
   for_pkg = "sysmgr"
   sources = [ "tee_manager.config" ]
diff --git a/src/security/tee_manager/meta/tee_manager.cmx b/src/security/tee_manager/meta/tee_manager.cmx
index fc308fe..7e4d7c1 100644
--- a/src/security/tee_manager/meta/tee_manager.cmx
+++ b/src/security/tee_manager/meta/tee_manager.cmx
@@ -7,6 +7,7 @@
             "class/tee"
         ],
         "features": [
+            "config-data",
             "isolated-persistent-storage"
         ],
         "services": [
diff --git a/src/security/tee_manager/src/config.rs b/src/security/tee_manager/src/config.rs
new file mode 100644
index 0000000..4cd0240
--- /dev/null
+++ b/src/security/tee_manager/src/config.rs
@@ -0,0 +1,27 @@
+// Copyright 2020 The Fuchsia Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+use {
+    anyhow::{Context, Error},
+    serde_derive::Deserialize,
+    serde_json,
+    std::{fs::File, io::BufReader},
+    uuid::Uuid,
+};
+
+const CONFIG_FILE: &'static str = "/config/data/tee_manager.config";
+
+#[derive(Deserialize)]
+pub struct Config {
+    pub application_uuids: Vec<Uuid>,
+}
+
+impl Config {
+    pub fn from_file() -> Result<Self, Error> {
+        Ok(serde_json::from_reader(BufReader::new(
+            File::open(CONFIG_FILE).context("Unable to open config")?,
+        ))
+        .context("Unable to parse config")?)
+    }
+}
diff --git a/src/security/tee_manager/src/device_server.rs b/src/security/tee_manager/src/device_server.rs
index 13b9fe35..8ed0dd0 100644
--- a/src/security/tee_manager/src/device_server.rs
+++ b/src/security/tee_manager/src/device_server.rs
@@ -7,7 +7,7 @@
     anyhow::{Context as _, Error},
     fidl::endpoints::{ClientEnd, ServerEnd},
     fidl_fuchsia_hardware_tee::DeviceConnectorProxy,
-    fuchsia_async as fasync, fuchsia_zircon as zx,
+    fidl_fuchsia_tee as fuchsia_tee, fuchsia_async as fasync, fuchsia_zircon as zx,
     std::path::PathBuf,
 };
 
@@ -27,7 +27,45 @@
 
     device_connector
         .connect_tee(Some(ClientEnd::new(zx_provider_client_end)), ServerEnd::new(channel))
-        .context("Could not connect to TEE over DeviceConnectorProxy")?;
+        .context("Could not connect to fuchsia.tee.Device over DeviceConnectorProxy")?;
 
     provider.serve(provider_server_chan).await
 }
+
+/// Serves a `fuchsia.tee.Application` protocol request by passing it through the
+/// `TeeDeviceConnection` and specifying the application UUID.
+pub async fn serve_application_passthrough(
+    mut uuid: fuchsia_tee::Uuid,
+    device_connector: DeviceConnectorProxy,
+    channel: zx::Channel,
+) -> Result<(), Error> {
+    // Create a ProviderServer to support the TEE driver
+    let provider = ProviderServer::try_new(PathBuf::new().join(STORAGE_DIR))?;
+    let (zx_provider_server_end, zx_provider_client_end) =
+        zx::Channel::create().context("Could not create Provider channel pair")?;
+
+    let provider_server_chan = fasync::Channel::from_channel(zx_provider_server_end)?;
+
+    device_connector
+        .connect_to_application(
+            &mut uuid,
+            Some(ClientEnd::new(zx_provider_client_end)),
+            ServerEnd::new(channel),
+        )
+        .context("Could not connect to fuchsia.tee.Application over DeviceConnectorProxy")?;
+
+    provider.serve(provider_server_chan).await
+}
+
+/// Serves a `fuchsia.tee.DeviceInfo` protocol request by passing it through the
+/// `TeeDeviceConnection`.
+pub async fn serve_device_info_passthrough(
+    device_connector: DeviceConnectorProxy,
+    channel: zx::Channel,
+) -> Result<(), Error> {
+    device_connector
+        .connect_to_device_info(ServerEnd::new(channel))
+        .context("Could not connect to fuchsia.tee.DeviceInfo over DeviceConnectorProxy")?;
+
+    Ok(())
+}
diff --git a/src/security/tee_manager/src/main.rs b/src/security/tee_manager/src/main.rs
index 6d87f00..3ecceeb4 100644
--- a/src/security/tee_manager/src/main.rs
+++ b/src/security/tee_manager/src/main.rs
@@ -4,15 +4,19 @@
 
 #![recursion_limit = "128"]
 
+mod config;
 mod device_server;
 mod provider_server;
 
 use {
-    crate::device_server::serve_passthrough,
+    crate::config::Config,
+    crate::device_server::{
+        serve_application_passthrough, serve_device_info_passthrough, serve_passthrough,
+    },
     anyhow::{format_err, Context as _, Error},
     fidl::endpoints::ServiceMarker,
     fidl_fuchsia_hardware_tee::{DeviceConnectorMarker, DeviceConnectorProxy},
-    fidl_fuchsia_tee::DeviceMarker,
+    fidl_fuchsia_tee::{self as fuchsia_tee, DeviceInfoMarker, DeviceMarker},
     fuchsia_async as fasync,
     fuchsia_component::server::ServiceFs,
     fuchsia_syslog as syslog,
@@ -21,12 +25,15 @@
     futures::{prelude::*, select, stream::FusedStream},
     io_util::{open_directory_in_namespace, OPEN_RIGHT_READABLE},
     std::path::PathBuf,
+    uuid::Uuid,
 };
 
 const DEV_TEE_PATH: &str = "/dev/class/tee";
 
 enum IncomingRequest {
     Device(zx::Channel),
+    Application(zx::Channel, fuchsia_tee::Uuid),
+    DeviceInfo(zx::Channel),
 }
 
 #[fasync::run_singlethreaded]
@@ -49,7 +56,26 @@
 
     let mut fs = ServiceFs::new_local();
     fs.dir("svc")
-        .add_service_at(DeviceMarker::NAME, |channel| Some(IncomingRequest::Device(channel)));
+        .add_service_at(DeviceMarker::NAME, |channel| Some(IncomingRequest::Device(channel)))
+        .add_service_at(DeviceInfoMarker::NAME, |channel| {
+            Some(IncomingRequest::DeviceInfo(channel))
+        });
+
+    match Config::from_file() {
+        Ok(config) => {
+            for app_uuid in config.application_uuids {
+                let service_name =
+                    format!("fuchsia.tee.Application.{}", app_uuid.to_hyphenated_ref());
+                fx_log_debug!("Serving {}", service_name);
+                let fidl_uuid = uuid_to_fuchsia_tee_uuid(&app_uuid);
+                fs.dir("svc").add_service_at(service_name, move |channel| {
+                    Some(IncomingRequest::Application(channel, fidl_uuid))
+                });
+            }
+        }
+        Err(error) => fx_log_warn!("Unable to serve applications: {:?}", error),
+    }
+
     fs.take_and_serve_directory_handle()?;
 
     serve(dev_connector_proxy, fs.fuse()).await
@@ -66,6 +92,13 @@
                 IncomingRequest::Device(channel) => {
                     serve_passthrough(dev_connector_proxy.clone(), channel).await
                 }
+                IncomingRequest::Application(channel, uuid) => {
+                    fx_log_trace!("Connecting application: {:?}", uuid);
+                    serve_application_passthrough(uuid, dev_connector_proxy.clone(), channel).await
+                }
+                IncomingRequest::DeviceInfo(channel) => {
+                    serve_device_info_passthrough(dev_connector_proxy.clone(), channel).await
+                }
             }
             .unwrap_or_else(|e| fx_log_err!("{:?}", e));
         });
@@ -111,6 +144,18 @@
     Ok(proxy)
 }
 
+/// Converts a `uuid::Uuid` to a `fidl_fuchsia_tee::Uuid`.
+fn uuid_to_fuchsia_tee_uuid(uuid: &Uuid) -> fuchsia_tee::Uuid {
+    let (time_low, time_mid, time_hi_and_version, clock_seq_and_node) = uuid.as_fields();
+
+    fuchsia_tee::Uuid {
+        time_low,
+        time_mid,
+        time_hi_and_version,
+        clock_seq_and_node: *clock_seq_and_node,
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use {
@@ -118,6 +163,7 @@
         fidl::{endpoints, Error},
         fidl_fuchsia_hardware_tee::DeviceConnectorRequest,
         fidl_fuchsia_io as fio,
+        fidl_fuchsia_tee::ApplicationMarker,
         fidl_fuchsia_tee_manager::ProviderProxy,
         fuchsia_zircon::HandleBased,
         fuchsia_zircon_status::Status,
@@ -163,13 +209,14 @@
         }
     }
 
-    async fn is_valid_storage(storage_proxy: &fio::DirectoryProxy) {
+    async fn assert_is_valid_storage(storage_proxy: &fio::DirectoryProxy) {
         let maybe_node_info = storage_proxy.describe().await;
         assert!(maybe_node_info.is_ok());
         let node_info = maybe_node_info.unwrap();
         assert!(is_directory(&node_info));
     }
 
+    // TODO(44664): Remove once ConnectTee is deprecated
     #[fasync::run_singlethreaded(test)]
     async fn connect() {
         let dev_connector = spawn_device_connector(|request| async move {
@@ -187,7 +234,7 @@
                         .into_proxy()
                         .expect("Failed to convert ClientEnd to ProviderProxy");
 
-                    is_valid_storage(&get_storage(&provider_proxy)).await;
+                    assert_is_valid_storage(&get_storage(&provider_proxy)).await;
 
                     tee_request
                         .close_with_epitaph(Status::OK)
@@ -221,6 +268,103 @@
     }
 
     #[fasync::run_singlethreaded(test)]
+    async fn connect_to_application() {
+        let app_uuid = uuid_to_fuchsia_tee_uuid(
+            &Uuid::parse_str("8aaaf200-2450-11e4-abe2-0002a5d5c51b").unwrap(),
+        );
+
+        let dev_connector = spawn_device_connector(move |request| async move {
+            match request {
+                DeviceConnectorRequest::ConnectToApplication {
+                    application_uuid,
+                    service_provider,
+                    application_request,
+                    control_handle: _,
+                } => {
+                    assert_eq!(application_uuid, app_uuid);
+                    assert!(service_provider.is_some());
+                    assert!(!application_request.channel().is_invalid_handle());
+
+                    let provider_proxy = service_provider
+                        .unwrap()
+                        .into_proxy()
+                        .expect("Failed to convert ClientEnd to ProviderProxy");
+
+                    assert_is_valid_storage(&get_storage(&provider_proxy)).await;
+
+                    application_request
+                        .close_with_epitaph(Status::OK)
+                        .expect("Unable to close tee_request");
+                }
+                _ => {
+                    assert!(false);
+                }
+            }
+        });
+
+        let (mut sender, receiver) = mpsc::channel::<IncomingRequest>(1);
+
+        fasync::spawn_local(async move {
+            let result = serve(dev_connector, receiver.fuse()).await;
+            assert!(result.is_ok(), "{}", result.unwrap_err());
+        });
+
+        let (app_client, app_server) = endpoints::create_endpoints::<ApplicationMarker>()
+            .expect("Failed to create Device endpoints");
+
+        let app_proxy =
+            app_client.into_proxy().expect("Failed to convert ClientEnd to DeviceProxy");
+        sender
+            .try_send(IncomingRequest::Application(app_server.into_channel(), app_uuid))
+            .expect("Unable to send Application Request");
+
+        let (result, _) = app_proxy.take_event_stream().into_future().await;
+        assert!(is_closed_with_status(result.unwrap().unwrap_err(), Status::OK));
+    }
+
+    #[fasync::run_singlethreaded(test)]
+    async fn connect_to_device_info() {
+        let dev_connector = spawn_device_connector(|request| async move {
+            match request {
+                DeviceConnectorRequest::ConnectToDeviceInfo {
+                    device_info_request,
+                    control_handle: _,
+                } => {
+                    assert!(!device_info_request.channel().is_invalid_handle());
+                    device_info_request
+                        .close_with_epitaph(Status::OK)
+                        .expect("Unable to close device_info_request");
+                }
+                _ => {
+                    assert!(false);
+                }
+            }
+        });
+
+        let (mut sender, receiver) = mpsc::channel::<IncomingRequest>(1);
+
+        fasync::spawn_local(async move {
+            let result = serve(dev_connector, receiver.fuse()).await;
+            assert!(result.is_ok(), "{}", result.unwrap_err());
+        });
+
+        let (device_info_client, device_info_server) =
+            endpoints::create_endpoints::<DeviceInfoMarker>()
+                .expect("Failed to create DeviceInfo endpoints");
+
+        let device_info_proxy = device_info_client
+            .into_proxy()
+            .expect("Failed to convert ClientEnd to DeviceInfoProxy");
+
+        sender
+            .try_send(IncomingRequest::DeviceInfo(device_info_server.into_channel()))
+            .expect("Unable to send DeviceInfo Request");
+
+        let (result, _) = device_info_proxy.take_event_stream().into_future().await;
+        assert!(is_closed_with_status(result.unwrap().unwrap_err(), Status::OK));
+    }
+
+    #[fasync::run_singlethreaded(test)]
     async fn tee_device_closed() {
         let (dev_connector_proxy, dev_connector_server) =
             fidl::endpoints::create_proxy::<DeviceConnectorMarker>()
diff --git a/src/security/tee_manager/tee_manager_config.gni b/src/security/tee_manager/tee_manager_config.gni
new file mode 100644
index 0000000..a281b9f
--- /dev/null
+++ b/src/security/tee_manager/tee_manager_config.gni
@@ -0,0 +1,55 @@
+# Copyright 2020 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config.gni")
+import("//build/json/validate_json.gni")
+
+# Validates a tee manager configuration file against a schema.
+#
+# Parameters
+#   config (required)
+#     This is the tee manager config file that needs to be validated.
+template("tee_manager_config_validate") {
+  validate_json(target_name) {
+    forward_variables_from(invoker,
+                           [
+                             "deps",
+                             "public_deps",
+                             "testonly",
+                             "visibility",
+                           ])
+
+    data = invoker.config
+    schema = "//src/security/tee_manager/tee_manager_config_schema.json"
+  }
+}
+
+# Packages a tee manager configuration after validating it.
+#
+# Parameters
+#   config (required)
+#     A file containing a configuration for the tee manager.
+template("tee_manager_config") {
+  validate_target = target_name + "_validate"
+
+  tee_manager_config_validate(validate_target) {
+    forward_variables_from(invoker,
+                           [
+                             "deps",
+                             "testonly",
+                             "visibility",
+                           ])
+
+    config = invoker.config
+  }
+
+  config_data(target_name) {
+    for_pkg = "tee_manager"
+    sources = [ rebase_path(invoker.config) ]
+
+    outputs = [ "tee_manager.config" ]
+
+    deps = [ ":${validate_target}" ]
+  }
+}
diff --git a/src/security/tee_manager/tee_manager_config_schema.json b/src/security/tee_manager/tee_manager_config_schema.json
new file mode 100644
index 0000000..53584c9
--- /dev/null
+++ b/src/security/tee_manager/tee_manager_config_schema.json
@@ -0,0 +1,23 @@
+{
+  "$schema": "http://json-schema.org/schema#",
+  "title": "Schema for tee manager configuration",
+  "definitions": {
+    "uuid": {
+      "type": "string",
+      "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"
+    }
+  },
+  "type": "object",
+  "properties": {
+    "application_uuids": {
+      "type": "array",
+      "items": {
+        "$ref": "#/definitions/uuid"
+      }
+    }
+  },
+  "required": [
+    "application_uuids"
+  ],
+  "additionalProperties": false
+}