-
Notifications
You must be signed in to change notification settings - Fork 166
Add Pluton attestation APIs #7262
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
d505f04
42c0a24
93fdcd9
c09adea
5463ecb
68aa9f0
1760031
45422cc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -61,6 +61,12 @@ namespace Azure { namespace Security { namespace Attestation { namespace Models | |
| * | ||
| */ | ||
| AZ_ATTESTATION_DLLEXPORT static const AttestationType Tpm; | ||
|
|
||
| /** | ||
| * @brief Specifies that this should apply to Pluton security processors. | ||
| * | ||
| */ | ||
| AZ_ATTESTATION_DLLEXPORT static const AttestationType Pluton; | ||
| }; | ||
|
|
||
| /** | ||
|
|
@@ -464,6 +470,15 @@ namespace Azure { namespace Security { namespace Attestation { namespace Models | |
| std::vector<uint8_t> TpmResult; | ||
| }; | ||
|
|
||
| /** @brief The result of a call to AttestPluton. | ||
| */ | ||
| struct PlutonAttestationResult final | ||
| { | ||
| /** @brief Attestation response data. | ||
| */ | ||
| std::vector<uint8_t> PlutonResult; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because the attestation result is unstructured data, it implies that a client cannot make any assumptions about the contents of the That restriction may be excessively limiting to your customers, but the documentation for this API is extremely minimal. I'm not 100% sure how any customer is going to be able to use this API.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Work to create public documentation describing the Pluton protocol messages is on our radar. Our primary goal for now is to unblock our partner team who are already acquainted with the request/response structures. |
||
| }; | ||
|
|
||
| /** | ||
| * @brief The PolicyModification enumeration represents the result of an attestation | ||
| * policy modification. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,149 @@ | ||
| // Copyright (c) Microsoft Corporation. | ||
| // Licensed under the MIT License. | ||
|
|
||
| #include "attestation_collateral.hpp" | ||
| #include "azure/attestation/attestation_administration_client.hpp" | ||
| #include "azure/attestation/attestation_client.hpp" | ||
|
|
||
| #include <azure/core/internal/json/json.hpp> | ||
| #include <azure/core/test/test_base.hpp> | ||
| #include <azure/identity/client_secret_credential.hpp> | ||
|
|
||
| #include <tuple> | ||
|
|
||
| #include <gtest/gtest.h> | ||
|
|
||
| using namespace Azure::Security::Attestation; | ||
| using namespace Azure::Security::Attestation::Models; | ||
| using namespace Azure::Core; | ||
|
|
||
| namespace Azure { namespace Security { namespace Attestation { namespace Test { | ||
|
|
||
| enum class PlutonInstanceType | ||
| { | ||
| Shared, | ||
| AAD, | ||
| Isolated | ||
| }; | ||
|
|
||
| // cspell: words plutonattestation | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Piling onto my previous comment: Non-beta Azure SDKs cannot depend on preview API versions. So until the attestation service GA's the 2026-03-11 API version, the attestation client can only be in beta release. |
||
| static const std::string PlutonApiVersion = "2026-03-11-preview"; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Normally clients don't specify the API version and instead use the current API version. It's surprising that the pluton attestation APIs require that the customer provide a unique API version. In general, the API version construct accepted by the Azure SDKs is flawed - the idea is that you could specify an older or newer API version on individual clients but it also meant that there could be no wire differences between the inputs and outputs of the service. But the API version defines the inputs and outputs of the service and any/all breaking changes in the wire API are required to have a version bump (thus for statically typed languages like C#, C++, Rust, Java this requires source changes on the SDK). So for statically typed languages, the use of the Api Version is strongly discouraged. Also, this locks your clients into a preview API which also means that you force the server to support a preview version forever (at least a decade). The Attestation service currently has to support the Beta1 preview because a partner team shipped client software that depended on a preview version. You really don't want to go there. Is there a reason you can't bump the required API version for the SDK?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My initial thought was to only expose the Pluton endpoint through a preview version because our partner team is still integrating the client component, and this SDK update is meant to unblock them. Is it preferable to have them use a preview version while they are still in development and then GA later on before shipping, or simply release 2026-03-11 as a stable API version from the start? Olga Kroshkina (@olkroshk) , Greg Kostal (@gkostal) , what do you folks think?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here's the bottom line: Having a public release of an Azure SDK that requires a preview API version is a violation of our guidelines on API versioning. So unfortunately we can't allow a public release of an SDK client that depends on a -preview API version for any of its functionality :(. However there's nothing stopping you from having as many beta releases as you want for your partner teams to test. But you do need to know that beta releases go into the beta package repository, so your partner teams either need to use the beta registry or they need to use a git submodule (or the moral equivalent of a git submodule) to get access to your SDK. And honestly that's a good thing - keep the Pluton APIs with a And once that is done, update the SDK to require the current API version for all the APIs (I noticed there have been a few stable releases of the attestation swagger/tsp files so it's probably past time to align the TSP files and the SDKs. ** OFFTOPIC ** |
||
|
|
||
| class PlutonAttestationTests : public Azure::Core::Test::TestBase { | ||
| public: | ||
| PlutonAttestationTests() { TestBase::SetUpTestSuiteLocal(AZURE_TEST_ASSETS_DIR); }; | ||
|
|
||
| protected: | ||
| std::shared_ptr<const Azure::Core::Credentials::TokenCredential> m_credential; | ||
| std::unique_ptr<AttestationAdministrationClient> m_adminClient; | ||
|
|
||
| // Create | ||
| virtual void SetUp() override | ||
| { | ||
| Azure::Core::Test::TestBase::SetUpTestBase(AZURE_TEST_RECORDING_DIR); | ||
| { | ||
| if (m_testContext.GetTestMode() != Azure::Core::Test::TestMode::PLAYBACK) | ||
| { | ||
| m_adminClient = std::make_unique<AttestationAdministrationClient>( | ||
| CreateAdminClient(PlutonInstanceType::AAD)); | ||
|
|
||
| // Set a minimal policy for Pluton attestation. | ||
| m_adminClient->SetAttestationPolicy( | ||
| AttestationType::Pluton, | ||
| "version=1.0; authorizationrules{=> permit();}; issuancerules{};"); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| virtual void TearDown() override | ||
| { | ||
| if (m_testContext.GetTestMode() != Azure::Core::Test::TestMode::PLAYBACK) | ||
| { | ||
| if (m_adminClient) | ||
| { | ||
| m_adminClient->ResetAttestationPolicy(AttestationType::Pluton); | ||
| } | ||
| } | ||
|
|
||
| // Make sure you call the base classes TearDown method to ensure recordings are made. | ||
| TestBase::TearDown(); | ||
| } | ||
|
|
||
| std::string GetInstanceUri(PlutonInstanceType instanceType) | ||
| { | ||
| if (instanceType == PlutonInstanceType::Shared) | ||
| { | ||
| std::string shortLocation(GetEnv("LOCATION_SHORT_NAME")); | ||
| return "https://shared" + shortLocation + "." + shortLocation + ".attest.azure.net"; | ||
| } | ||
| else if (instanceType == PlutonInstanceType::AAD) | ||
| { | ||
| return GetEnv("ATTESTATION_AAD_URL"); | ||
| } | ||
| else if (instanceType == PlutonInstanceType::Isolated) | ||
| { | ||
| return GetEnv("ATTESTATION_ISOLATED_URL"); | ||
| } | ||
| throw std::runtime_error("Unkown instance type."); | ||
| } | ||
|
|
||
| AttestationTokenValidationOptions GetTokenValidationOptions() | ||
| { | ||
| AttestationTokenValidationOptions returnValue; | ||
| if (m_testContext.IsPlaybackMode()) | ||
| { | ||
| // Skip validating time stamps if using recordings. | ||
| returnValue.ValidateNotBeforeTime = false; | ||
| returnValue.ValidateExpirationTime = false; | ||
| } | ||
| else | ||
| { | ||
| returnValue.TimeValidationSlack = 10s; | ||
| } | ||
| return returnValue; | ||
| } | ||
|
|
||
| AttestationClient CreateClient(PlutonInstanceType instanceType) | ||
| { | ||
| // `InitClientOptions` takes care of setting up Record&Playback. | ||
| AttestationClientOptions options = InitClientOptions<AttestationClientOptions>(); | ||
| options.ApiVersion = PlutonApiVersion; | ||
| options.TokenValidationOptions = GetTokenValidationOptions(); | ||
| auto credential = GetTestCredential(); | ||
| return AttestationClient::Create(GetInstanceUri(instanceType), credential, options); | ||
| } | ||
|
|
||
| AttestationAdministrationClient CreateAdminClient(PlutonInstanceType instanceType) | ||
| { | ||
| // `InitTestClient` takes care of setting up Record&Playback. | ||
| AttestationAdministrationClientOptions options | ||
| = InitClientOptions<AttestationAdministrationClientOptions>(); | ||
| options.ApiVersion = PlutonApiVersion; | ||
| options.TokenValidationOptions = GetTokenValidationOptions(); | ||
| auto credential = GetTestCredential(); | ||
| return AttestationAdministrationClient::Create( | ||
| GetInstanceUri(instanceType), credential, options); | ||
| } | ||
| }; | ||
|
|
||
| TEST_F(PlutonAttestationTests, AttestPluton_LIVEONLY_) | ||
| { | ||
| auto client(CreateClient(PlutonInstanceType::AAD)); | ||
|
|
||
| std::string plutonPayload = R"({"payload": { "type": "pluton" } })"; | ||
| auto response( | ||
| client.AttestPluton(std::vector<uint8_t>(plutonPayload.begin(), plutonPayload.end()))); | ||
|
|
||
| // Verify the response contains bytes. | ||
| EXPECT_FALSE(response.Value.PlutonResult.empty()); | ||
|
|
||
| // Parse the response to verify it's valid JSON with expected structure. | ||
| Azure::Core::Json::_internal::json parsedResponse( | ||
| Azure::Core::Json::_internal::json::parse(response.Value.PlutonResult)); | ||
| EXPECT_TRUE(parsedResponse.contains("payload")); | ||
| EXPECT_TRUE(parsedResponse["payload"].is_object()); | ||
| EXPECT_TRUE(parsedResponse["payload"].contains("challenge")); | ||
| EXPECT_TRUE(parsedResponse["payload"].contains("service_context")); | ||
| } | ||
|
|
||
| }}}} // namespace Azure::Security::Attestation::Test | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since the AttestPluton depends on a custom API version, document the requirement of the custom API version.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note added.