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();
+}