From e41a678889886ffb56057250172355449e5f36b7 Mon Sep 17 00:00:00 2001 From: Tom Binder Date: Thu, 9 Jan 2025 13:40:50 +0000 Subject: [PATCH] Add centralized C++ utilities for oak::Variant. Library can be imported via copybara and then used in the hostlibs. This will make the present ad-hoc solution in the hostlibs obsolete, and also keeps anthing variant-related (in particular the UUID definitions) with the proto definitions in open source. Bug: 373829620 Change-Id: I0e715126807a96fc2b48662350011d3e031a0521 --- cc/utils/variant/BUILD | 45 ++++++++++ cc/utils/variant/uuids.h | 41 +++++++++ cc/utils/variant/variant.cc | 139 +++++++++++++++++++++++++++++++ cc/utils/variant/variant.h | 38 +++++++++ cc/utils/variant/variant_test.cc | 65 +++++++++++++++ 5 files changed, 328 insertions(+) create mode 100644 cc/utils/variant/BUILD create mode 100644 cc/utils/variant/uuids.h create mode 100644 cc/utils/variant/variant.cc create mode 100644 cc/utils/variant/variant.h create mode 100644 cc/utils/variant/variant_test.cc diff --git a/cc/utils/variant/BUILD b/cc/utils/variant/BUILD new file mode 100644 index 0000000000..76e98b4a23 --- /dev/null +++ b/cc/utils/variant/BUILD @@ -0,0 +1,45 @@ +# +# Copyright 2025 The Project Oak Authors +# +# 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( + default_visibility = ["//:default_visibility"], + licenses = ["notice"], +) + +cc_library( + name = "variant", + srcs = [ + "uuids.h", + "variant.cc", + ], + hdrs = ["variant.h"], + deps = [ + "//proto:variant_cc_proto", + "//proto/attestation:endorsement_cc_proto", + "@com_google_absl//absl/log:check", + "@com_google_absl//absl/strings", + ], +) + +cc_test( + name = "variant_test", + srcs = ["variant_test.cc"], + deps = [ + ":variant", + "//proto/attestation:endorsement_cc_proto", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/cc/utils/variant/uuids.h b/cc/utils/variant/uuids.h new file mode 100644 index 0000000000..3aa127560b --- /dev/null +++ b/cc/utils/variant/uuids.h @@ -0,0 +1,41 @@ +/* + * Copyright 2025 The Project Oak Authors + * + * 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. + */ + +#ifndef CC_UTILS_VARIANT_UUIDS_H_ +#define CC_UTILS_VARIANT_UUIDS_H_ + +#include "absl/strings/string_view.h" + +namespace oak { +namespace internal { + +constexpr absl::string_view kAmdSevSnpPlatformEndorsementUuid = + "5a12d00f-48a0-4224-bff4-975c7657438f"; +constexpr absl::string_view kFirmwareEndorsementUuid = + "de4a0d55-60ea-4dc6-abd1-09ed744f80ea"; +constexpr absl::string_view kKernelEndorsementUuid = + "89511d65-5d35-4601-900b-1e6dbaf842b6"; +constexpr absl::string_view kSystemEndorsementUuid = + "4722655d-963d-4fc9-8443-f14571dd32a2"; +constexpr absl::string_view kContainerEndorsementUuid = + "7297a51f-a05d-49a1-afdb-64cdee07862d"; +constexpr absl::string_view kApplicationEndorsementUuid = + "e84ed714-669d-430a-a60f-8a651e5a5503"; + +} // namespace internal +} // namespace oak + +#endif // CC_UTILS_VARIANT_UUIDS_H_ diff --git a/cc/utils/variant/variant.cc b/cc/utils/variant/variant.cc new file mode 100644 index 0000000000..d094e04d37 --- /dev/null +++ b/cc/utils/variant/variant.cc @@ -0,0 +1,139 @@ +/* + * Copyright 2025 The Project Oak Authors + * + * 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 "cc/utils/variant/variant.h" + +#include +#include + +#include "absl/log/check.h" +#include "absl/strings/escaping.h" +#include "absl/strings/str_replace.h" +#include "absl/strings/string_view.h" +#include "cc/utils/variant/uuids.h" +#include "proto/attestation/endorsement.pb.h" + +namespace oak { +namespace { + +using ::oak::attestation::v1::AmdSevSnpEndorsement; +using ::oak::attestation::v1::ApplicationEndorsement; +using ::oak::attestation::v1::ContainerEndorsement; +using ::oak::attestation::v1::FirmwareEndorsement; +using ::oak::attestation::v1::KernelEndorsement; +using ::oak::attestation::v1::SystemEndorsement; + +std::string RawUuid(absl::string_view uuidHex) { + std::string strip = absl::StrReplaceAll(uuidHex, {{"-", ""}}); + std::string raw; + CHECK(absl::HexStringToBytes(strip, &raw)); + CHECK_EQ(raw.length(), 16u); // 128 bits + return raw; +} + +template +Variant CreateVariant(absl::string_view uuidHex, const T& message) { + std::string serialized; + CHECK(message.SerializeToString(&serialized)); + + Variant variant; + variant.set_id(RawUuid(uuidHex)); + variant.set_value(serialized); + return variant; +} + +template +std::optional ParseVariant(absl::string_view uuidHex, + const Variant& variant) { + if (RawUuid(uuidHex) != variant.id()) { + return std::nullopt; + } + + T message; + message.ParseFromString(variant.value()); + return message; +} + +} // namespace + +template <> +Variant ToVariant(const AmdSevSnpEndorsement& message) { + return CreateVariant(internal::kAmdSevSnpPlatformEndorsementUuid, message); +} + +template <> +Variant ToVariant(const FirmwareEndorsement& message) { + return CreateVariant(internal::kFirmwareEndorsementUuid, message); +} + +template <> +Variant ToVariant(const KernelEndorsement& message) { + return CreateVariant(internal::kKernelEndorsementUuid, message); +} + +template <> +Variant ToVariant(const SystemEndorsement& message) { + return CreateVariant(internal::kSystemEndorsementUuid, message); +} + +template <> +Variant ToVariant(const ContainerEndorsement& message) { + return CreateVariant(internal::kContainerEndorsementUuid, message); +} + +template <> +Variant ToVariant( + const ApplicationEndorsement& message) { + return CreateVariant(internal::kApplicationEndorsementUuid, message); +} + +template <> +std::optional FromVariant(const Variant& variant) { + return ParseVariant( + internal::kAmdSevSnpPlatformEndorsementUuid, variant); +} + +template <> +std::optional FromVariant(const Variant& variant) { + return ParseVariant(internal::kFirmwareEndorsementUuid, + variant); +} + +template <> +std::optional FromVariant(const Variant& variant) { + return ParseVariant(internal::kKernelEndorsementUuid, + variant); +} + +template <> +std::optional FromVariant(const Variant& variant) { + return ParseVariant(internal::kSystemEndorsementUuid, + variant); +} + +template <> +std::optional FromVariant(const Variant& variant) { + return ParseVariant(internal::kContainerEndorsementUuid, + variant); +} + +template <> +std::optional FromVariant(const Variant& variant) { + return ParseVariant( + internal::kApplicationEndorsementUuid, variant); +} + +} // namespace oak diff --git a/cc/utils/variant/variant.h b/cc/utils/variant/variant.h new file mode 100644 index 0000000000..c069e0cfaa --- /dev/null +++ b/cc/utils/variant/variant.h @@ -0,0 +1,38 @@ +/* + * Copyright 2025 The Project Oak Authors + * + * 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. + */ + +#ifndef CC_UTILS_VARIANT_VARIANT_H_ +#define CC_UTILS_VARIANT_VARIANT_H_ + +#include + +#include "proto/variant.pb.h" + +namespace oak { + +// Converts a proto message to a Variant proto. +// Compiles as long as we have UUID coverage for message type T. +template +Variant ToVariant(const T& message); + +// Converts a Variant proto to a proto message. +// Returns empty whenever `variant` does not contain type T. +template +std::optional FromVariant(const Variant& variant); + +} // namespace oak + +#endif // CC_UTILS_VARIANT_VARIANT_H_ diff --git a/cc/utils/variant/variant_test.cc b/cc/utils/variant/variant_test.cc new file mode 100644 index 0000000000..907a5dc5a7 --- /dev/null +++ b/cc/utils/variant/variant_test.cc @@ -0,0 +1,65 @@ +/* + * Copyright 2025 The Project Oak Authors + * + * 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 "cc/utils/variant/variant.h" + +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "proto/attestation/endorsement.pb.h" + +namespace oak { +namespace { + +using ::oak::attestation::v1::AmdSevSnpEndorsement; +using ::oak::attestation::v1::ApplicationEndorsement; +using ::oak::attestation::v1::ContainerEndorsement; +using ::oak::attestation::v1::FirmwareEndorsement; +using ::oak::attestation::v1::KernelEndorsement; +using ::oak::attestation::v1::SystemEndorsement; + +template +class VariantTest : public testing::Test { + public: + bool CompareProtos(const T& proto1, const T& proto2) { + std::string serialized1, serialized2; + EXPECT_TRUE(proto1.SerializeToString(&serialized1)); + EXPECT_TRUE(proto2.SerializeToString(&serialized2)); + return serialized1 == serialized2; + } + + T CreateInstance() { return T(); } +}; + +TYPED_TEST_SUITE_P(VariantTest); + +TYPED_TEST_P(VariantTest, Inverse) { + TypeParam expected = this->CreateInstance(); + TypeParam actual = FromVariant(ToVariant(expected)).value(); + EXPECT_TRUE(this->CompareProtos(actual, expected)); +} + +REGISTER_TYPED_TEST_SUITE_P(VariantTest, Inverse); + +using TestTypes = testing::Types; + +INSTANTIATE_TYPED_TEST_SUITE_P(TypedTestSuite, VariantTest, TestTypes); + +} // namespace +} // namespace oak