Add helper library to get remotely provisioned key

The code is mostly from credstore. The intention here is that we replace
that code with a common library.

Test: librkp_support_test
Change-Id: I28ebc5a253c037277dad6d39b761b4e8aa4347e8
diff --git a/provisioner/support/Android.bp b/provisioner/support/Android.bp
new file mode 100644
index 0000000..778b1e0
--- /dev/null
+++ b/provisioner/support/Android.bp
@@ -0,0 +1,64 @@
+// Copyright (C) 2023 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.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["system_security_license"],
+}
+
+cc_defaults {
+    name: "librkp_support_defaults",
+    static_libs: [
+        "android.hardware.security.rkp-V3-cpp",
+        "android.security.rkp_aidl-cpp",
+    ],
+    shared_libs: [
+        "libbase",
+        "libbinder",
+        "libutils",
+        "libvintf",
+    ],
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+    ],
+}
+
+cc_library {
+    name: "librkp_support",
+    defaults: ["librkp_support_defaults"],
+    srcs: [
+        "rkpd_client.cpp",
+    ],
+    export_include_dirs: ["include"],
+}
+
+cc_test {
+    name: "librkp_support_test",
+    defaults: [
+        "librkp_support_defaults",
+        "use_libaidlvintf_gtest_helper_static",
+    ],
+    srcs: ["test.cpp"],
+    static_libs: [
+        "librkp_support",
+    ],
+    test_suites: ["general-tests"],
+    require_root: true,
+}
diff --git a/provisioner/support/TEST_MAPPING b/provisioner/support/TEST_MAPPING
new file mode 100644
index 0000000..fc30104
--- /dev/null
+++ b/provisioner/support/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "librkp_support_test"
+    }
+  ]
+}
diff --git a/provisioner/support/include/rkp/support/rkpd_client.h b/provisioner/support/include/rkp/support/rkpd_client.h
new file mode 100644
index 0000000..5a7fe6e
--- /dev/null
+++ b/provisioner/support/include/rkp/support/rkpd_client.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2022, 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 <future>
+#include <optional>
+
+#include <android/hardware/security/keymint/IRemotelyProvisionedComponent.h>
+#include <android/security/rkp/RemotelyProvisionedKey.h>
+
+namespace android::security::rkp::support {
+
+using ::android::hardware::security::keymint::IRemotelyProvisionedComponent;
+using ::android::security::rkp::RemotelyProvisionedKey;
+
+// Callers of getRpcKeyFuture() and getRpcKey() need at least two threads to
+// retrieve the key, one to asynchronously handle binder callbacks and one to
+// wait on the future.
+std::optional<std::future<std::optional<RemotelyProvisionedKey>>>
+getRpcKeyFuture(const sp<IRemotelyProvisionedComponent>& rpc, int32_t keyId);
+
+std::optional<RemotelyProvisionedKey> getRpcKey(const sp<IRemotelyProvisionedComponent>& rpc,
+                                                int32_t keyId, int32_t timeout_sec = 10);
+
+}  // namespace android::security::rkp::support
diff --git a/provisioner/support/rkpd_client.cpp b/provisioner/support/rkpd_client.cpp
new file mode 100644
index 0000000..0643457
--- /dev/null
+++ b/provisioner/support/rkpd_client.cpp
@@ -0,0 +1,219 @@
+/*
+ * Copyright (c) 2019, 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.
+ */
+
+#define LOG_TAG "rkpd_client"
+
+#include <atomic>
+
+#include <android-base/logging.h>
+#include <android/security/rkp/BnGetKeyCallback.h>
+#include <android/security/rkp/BnGetRegistrationCallback.h>
+#include <android/security/rkp/IGetKeyCallback.h>
+#include <android/security/rkp/IRemoteProvisioning.h>
+#include <binder/IServiceManager.h>
+#include <binder/Status.h>
+#include <rkp/support/rkpd_client.h>
+#include <vintf/VintfObject.h>
+
+namespace android::security::rkp::support {
+namespace {
+
+using ::android::binder::Status;
+using ::android::hardware::security::keymint::IRemotelyProvisionedComponent;
+using ::android::hardware::security::keymint::RpcHardwareInfo;
+using ::android::security::rkp::BnGetKeyCallback;
+using ::android::security::rkp::BnGetRegistrationCallback;
+using ::android::security::rkp::IGetKeyCallback;
+using ::android::security::rkp::IRegistration;
+using ::android::security::rkp::IRemoteProvisioning;
+using ::android::security::rkp::RemotelyProvisionedKey;
+
+constexpr const char* kRemoteProvisioningServiceName = "remote_provisioning";
+
+std::optional<std::string> getRpcId(const sp<IRemotelyProvisionedComponent>& rpc) {
+    RpcHardwareInfo rpcHwInfo;
+    Status status = rpc->getHardwareInfo(&rpcHwInfo);
+    if (!status.isOk()) {
+        LOG(ERROR) << "Error getting remotely provisioned component hardware info: " << status;
+        return std::nullopt;
+    }
+
+    if (!rpcHwInfo.uniqueId) {
+        LOG(ERROR) << "Remotely provisioned component is missing a unique id. "
+                   << "This is a bug in the vendor implementation.";
+        return std::nullopt;
+    }
+
+    return *rpcHwInfo.uniqueId;
+}
+
+std::optional<String16> findRpcNameById(std::string_view targetRpcId) {
+    auto deviceManifest = vintf::VintfObject::GetDeviceHalManifest();
+    auto instances = deviceManifest->getAidlInstances("android.hardware.security.keymint",
+                                                      "IRemotelyProvisionedComponent");
+    for (const std::string& instance : instances) {
+        auto rpcName =
+            IRemotelyProvisionedComponent::descriptor + String16("/") + String16(instance.c_str());
+        sp<IRemotelyProvisionedComponent> rpc =
+            android::waitForService<IRemotelyProvisionedComponent>(rpcName);
+
+        auto rpcId = getRpcId(rpc);
+        if (!rpcId) {
+            continue;
+        }
+        if (*rpcId == targetRpcId) {
+            return rpcName;
+        }
+    }
+
+    LOG(ERROR) << "Remotely provisioned component with given unique ID: " << targetRpcId
+               << " not found";
+    return std::nullopt;
+}
+
+std::optional<String16> getRpcName(const sp<IRemotelyProvisionedComponent>& rpc) {
+    std::optional<std::string> targetRpcId = getRpcId(rpc);
+    if (!targetRpcId) {
+        return std::nullopt;
+    }
+    return findRpcNameById(*targetRpcId);
+}
+
+class GetKeyCallback : public BnGetKeyCallback {
+  public:
+    GetKeyCallback(std::promise<std::optional<RemotelyProvisionedKey>> keyPromise)
+        : keyPromise_(std::move(keyPromise)), called_() {}
+
+    Status onSuccess(const RemotelyProvisionedKey& key) override {
+        if (called_.test_and_set()) {
+            return Status::ok();
+        }
+        keyPromise_.set_value(key);
+        return Status::ok();
+    }
+    Status onCancel() override {
+        if (called_.test_and_set()) {
+            return Status::ok();
+        }
+        LOG(ERROR) << "GetKeyCallback cancelled";
+        keyPromise_.set_value(std::nullopt);
+        return Status::ok();
+    }
+    Status onError(IGetKeyCallback::ErrorCode error, const String16& description) override {
+        if (called_.test_and_set()) {
+            return Status::ok();
+        }
+        LOG(ERROR) << "GetKeyCallback failed: " << static_cast<int>(error) << ", " << description;
+        keyPromise_.set_value(std::nullopt);
+        return Status::ok();
+    }
+
+  private:
+    std::promise<std::optional<RemotelyProvisionedKey>> keyPromise_;
+    // This callback can only be called into once
+    std::atomic_flag called_;
+};
+
+class GetRegistrationCallback : public BnGetRegistrationCallback {
+  public:
+    GetRegistrationCallback(std::promise<std::optional<RemotelyProvisionedKey>> keyPromise,
+                            uint32_t keyId)
+        : keyPromise_(std::move(keyPromise)), keyId_(keyId), called_() {}
+
+    Status onSuccess(const sp<IRegistration>& registration) override {
+        if (called_.test_and_set()) {
+            return Status::ok();
+        }
+        auto cb = sp<GetKeyCallback>::make(std::move(keyPromise_));
+        auto status = registration->getKey(keyId_, cb);
+        if (!status.isOk()) {
+            cb->onError(IGetKeyCallback::ErrorCode::ERROR_UNKNOWN,
+                        String16("Failed to register GetKeyCallback"));
+        }
+        return Status::ok();
+    }
+    Status onCancel() override {
+        if (called_.test_and_set()) {
+            return Status::ok();
+        }
+        LOG(ERROR) << "GetRegistrationCallback cancelled";
+        keyPromise_.set_value(std::nullopt);
+        return Status::ok();
+    }
+    Status onError(const String16& error) override {
+        if (called_.test_and_set()) {
+            return Status::ok();
+        }
+        LOG(ERROR) << "GetRegistrationCallback failed: " << error;
+        keyPromise_.set_value(std::nullopt);
+        return Status::ok();
+    }
+
+  private:
+    std::promise<std::optional<RemotelyProvisionedKey>> keyPromise_;
+    int32_t keyId_;
+    // This callback can only be called into once
+    std::atomic_flag called_;
+};
+
+}  // namespace
+
+std::optional<std::future<std::optional<RemotelyProvisionedKey>>>
+getRpcKeyFuture(const sp<IRemotelyProvisionedComponent>& rpc, int32_t keyId) {
+    std::promise<std::optional<RemotelyProvisionedKey>> keyPromise;
+    auto keyFuture = keyPromise.get_future();
+
+    auto rpcName = getRpcName(rpc);
+    if (!rpcName) {
+        LOG(ERROR) << "Failed to get IRemotelyProvisionedComponent name";
+        return std::nullopt;
+    }
+
+    sp<IRemoteProvisioning> remoteProvisioning =
+        android::waitForService<IRemoteProvisioning>(String16(kRemoteProvisioningServiceName));
+    if (!remoteProvisioning) {
+        LOG(ERROR) << "Failed to get IRemoteProvisioning HAL";
+        return std::nullopt;
+    }
+
+    auto cb = sp<GetRegistrationCallback>::make(std::move(keyPromise), keyId);
+    Status status = remoteProvisioning->getRegistration(*rpcName, cb);
+    if (!status.isOk()) {
+        LOG(ERROR) << "Failed getRegistration()";
+        return std::nullopt;
+    }
+
+    return keyFuture;
+}
+
+std::optional<RemotelyProvisionedKey> getRpcKey(const sp<IRemotelyProvisionedComponent>& rpc,
+                                                int32_t keyId, int32_t timeout_sec) {
+    auto rpcKeyFuture = getRpcKeyFuture(rpc, keyId);
+    if (!rpcKeyFuture) {
+        LOG(ERROR) << "Failed getRpcKeyFuture()";
+        return std::nullopt;
+    }
+
+    auto timeout = std::chrono::seconds(timeout_sec);
+    if (rpcKeyFuture->wait_for(timeout) != std::future_status::ready) {
+        LOG(ERROR) << "Waiting for remotely provisioned attestation key timed out";
+        return std::nullopt;
+    }
+
+    return rpcKeyFuture->get();
+}
+
+}  // namespace android::security::rkp::support
diff --git a/provisioner/support/test.cpp b/provisioner/support/test.cpp
new file mode 100644
index 0000000..418eab9
--- /dev/null
+++ b/provisioner/support/test.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2023 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 <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <android/hardware/security/keymint/IRemotelyProvisionedComponent.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <gtest/gtest.h>
+#include <rkp/support/rkpd_client.h>
+
+using ::android::getAidlHalInstanceNames;
+using ::android::sp;
+using ::android::String16;
+using ::android::hardware::security::keymint::IRemotelyProvisionedComponent;
+using ::android::security::rkp::RemotelyProvisionedKey;
+using ::android::security::rkp::support::getRpcKey;
+
+// TODO(b/272600606): Add tests for error cases
+class RkpdClientTest : public testing::TestWithParam<std::string> {
+  public:
+    virtual void SetUp() override {
+        auto rpcName = String16(GetParam().c_str());
+        rpc_ = android::waitForService<IRemotelyProvisionedComponent>(rpcName);
+        ASSERT_NE(rpc_, nullptr);
+    }
+
+    sp<IRemotelyProvisionedComponent> rpc_;
+};
+
+TEST_P(RkpdClientTest, getRpcKey) {
+    std::optional<RemotelyProvisionedKey> key = getRpcKey(rpc_, /*keyId=*/0);
+
+    ASSERT_TRUE(key.has_value()) << "Failed to get remotely provisioned attestation key";
+    ASSERT_FALSE(key->keyBlob.empty()) << "Key blob is empty";
+    ASSERT_FALSE(key->encodedCertChain.empty()) << "Certificate is empty";
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    PerInstance, RkpdClientTest,
+    testing::ValuesIn(getAidlHalInstanceNames(IRemotelyProvisionedComponent::descriptor)),
+    ::android::PrintInstanceNameToString);
+
+int main(int argc, char** argv) {
+    testing::InitGoogleTest(&argc, argv);
+
+    // We need one thread to issue requests to RKPD and one to handle
+    // asynchronous responses from RKPD.
+    android::ProcessState::self()->setThreadPoolMaxThreadCount(2);
+    android::ProcessState::self()->startThreadPool();
+    return RUN_ALL_TESTS();
+}