From d505f042e3811844397b929fb5479484f842dadc Mon Sep 17 00:00:00 2001 From: "Teresa Nguyen (from Dev Box)" Date: Tue, 21 Jul 2026 13:28:43 -0500 Subject: [PATCH 1/6] add pluton --- .../azure/attestation/attestation_client.hpp | 14 ++++++++ .../attestation/attestation_client_models.hpp | 9 ++++++ .../attestation_client_options.hpp | 6 ++++ .../src/attestation_client.cpp | 32 +++++++++++++++++-- .../attestation_deserializers_private.cpp | 8 ++--- .../attestation_deserializers_private.hpp | 2 +- 6 files changed, 64 insertions(+), 7 deletions(-) diff --git a/sdk/attestation/azure-security-attestation/inc/azure/attestation/attestation_client.hpp b/sdk/attestation/azure-security-attestation/inc/azure/attestation/attestation_client.hpp index 11a54c480c..ede265e3c0 100644 --- a/sdk/attestation/azure-security-attestation/inc/azure/attestation/attestation_client.hpp +++ b/sdk/attestation/azure-security-attestation/inc/azure/attestation/attestation_client.hpp @@ -247,6 +247,20 @@ namespace Azure { namespace Security { namespace Attestation { AttestTpmOptions const& options = AttestTpmOptions{}, Azure::Core::Context const& context = Azure::Core::Context{}) const; + /** + * @brief Sends Pluton-based attestation data to the service. + * + * @param dataToAttest - Attestation request data. + * @param options - Options to the attestation request. + * @param context - Context for the operation. + * + * @return Response - The result of the attestation operation + */ + Response AttestPluton( + std::vector const& dataToAttest, + AttestPlutonOptions const& options = AttestPlutonOptions{}, + Azure::Core::Context const& context = Azure::Core::Context{}) const; + private: Azure::Core::Url m_endpoint; std::string m_apiVersion; diff --git a/sdk/attestation/azure-security-attestation/inc/azure/attestation/attestation_client_models.hpp b/sdk/attestation/azure-security-attestation/inc/azure/attestation/attestation_client_models.hpp index 2332b94384..9fd39507ca 100644 --- a/sdk/attestation/azure-security-attestation/inc/azure/attestation/attestation_client_models.hpp +++ b/sdk/attestation/azure-security-attestation/inc/azure/attestation/attestation_client_models.hpp @@ -464,6 +464,15 @@ namespace Azure { namespace Security { namespace Attestation { namespace Models std::vector TpmResult; }; + /** @brief The result of a call to AttestPluton. + */ + struct PlutonAttestationResult final + { + /** @brief Attestation response data. + */ + std::vector PlutonResult; + }; + /** * @brief The PolicyModification enumeration represents the result of an attestation * policy modification. diff --git a/sdk/attestation/azure-security-attestation/inc/azure/attestation/attestation_client_options.hpp b/sdk/attestation/azure-security-attestation/inc/azure/attestation/attestation_client_options.hpp index 29d15bb154..62f9d1037c 100644 --- a/sdk/attestation/azure-security-attestation/inc/azure/attestation/attestation_client_options.hpp +++ b/sdk/attestation/azure-security-attestation/inc/azure/attestation/attestation_client_options.hpp @@ -264,6 +264,12 @@ namespace Azure { namespace Security { namespace Attestation { { }; + /** @brief Parameters sent to the attestation service for the AttestPluton API. + */ + struct AttestPlutonOptions final + { + }; + /** @brief The AttestationSigningKey represents a tuple of asymmetric private cryptographic key * and X.509 certificate wrapping the public key contained in the certificate. * diff --git a/sdk/attestation/azure-security-attestation/src/attestation_client.cpp b/sdk/attestation/azure-security-attestation/src/attestation_client.cpp index 17a4f432ed..f5ba83627f 100644 --- a/sdk/attestation/azure-security-attestation/src/attestation_client.cpp +++ b/sdk/attestation/azure-security-attestation/src/attestation_client.cpp @@ -211,7 +211,7 @@ Azure::Response AttestationClient::AttestTpm( auto tracingContext(m_tracingFactory.CreateTracingContext("AttestTpm", context)); try { - std::string jsonToSend = TpmDataSerializer::Serialize(dataToAttest); + std::string jsonToSend = TpmAndPlutonDataSerializer::Serialize(dataToAttest); auto encodedVector = std::vector(jsonToSend.begin(), jsonToSend.end()); Azure::Core::IO::MemoryBodyStream stream(encodedVector); @@ -221,7 +221,7 @@ Azure::Response AttestationClient::AttestTpm( // Send the request to the service. auto response = AttestationCommonRequest::SendRequest(*m_pipeline, request, tracingContext.Context); - std::vector returnedBody{TpmDataSerializer::Deserialize(response)}; + std::vector returnedBody{TpmAndPlutonDataSerializer::Deserialize(response)}; return Response(TpmAttestationResult{returnedBody}, std::move(response)); } catch (std::runtime_error const& ex) @@ -231,6 +231,34 @@ Azure::Response AttestationClient::AttestTpm( } } +Azure::Response AttestationClient::AttestPluton( + std::vector const& dataToAttest, + AttestPlutonOptions const&, + Azure::Core::Context const& context) const +{ + auto tracingContext(m_tracingFactory.CreateTracingContext("AttestPluton", context)); + try + { + std::string jsonToSend = TpmAndPlutonDataSerializer::Serialize(dataToAttest); + auto encodedVector = std::vector(jsonToSend.begin(), jsonToSend.end()); + Azure::Core::IO::MemoryBodyStream stream(encodedVector); + + auto request = AttestationCommonRequest::CreateRequest( + m_endpoint, m_apiVersion, HttpMethod::Post, {"attest/Pluton"}, &stream); + + // Send the request to the service. + auto response + = AttestationCommonRequest::SendRequest(*m_pipeline, request, tracingContext.Context); + std::vector returnedBody{TpmAndPlutonDataSerializer::Deserialize(response)}; + return Response(PlutonAttestationResult{returnedBody}, std::move(response)); + } + catch (std::runtime_error const& ex) + { + tracingContext.Span.AddEvent(ex); + throw; + } +} + namespace { std::shared_timed_mutex SharedStateLock; } diff --git a/sdk/attestation/azure-security-attestation/src/private/attestation_deserializers_private.cpp b/sdk/attestation/azure-security-attestation/src/private/attestation_deserializers_private.cpp index 5a3c77726c..48885bf7e4 100644 --- a/sdk/attestation/azure-security-attestation/src/private/attestation_deserializers_private.cpp +++ b/sdk/attestation/azure-security-attestation/src/private/attestation_deserializers_private.cpp @@ -396,13 +396,13 @@ namespace Azure { namespace Security { namespace Attestation { namespace _detail returnValue.CertificateThumbprint, jsonResult, "x-ms-certificate-thumbprint"); return returnValue; } - std::string TpmDataSerializer::Serialize(std::vector const& tpmData) + std::string TpmAndPlutonDataSerializer::Serialize(std::vector const& tpmData) { Azure::Core::Json::_internal::json jsonData; jsonData["data"] = Azure::Core::_internal::Base64Url::Base64UrlEncode(tpmData); return jsonData.dump(); } - std::vector TpmDataSerializer::Deserialize( + std::vector TpmAndPlutonDataSerializer::Deserialize( Azure::Core::Json::_internal::json const& jsonData) { std::vector returnValue; @@ -410,10 +410,10 @@ namespace Azure { namespace Security { namespace Attestation { namespace _detail returnValue, jsonData, "data", Azure::Core::_internal::Base64Url::Base64UrlDecode); return returnValue; } - std::vector TpmDataSerializer::Deserialize( + std::vector TpmAndPlutonDataSerializer::Deserialize( std::unique_ptr const& response) { - return TpmDataSerializer::Deserialize( + return TpmAndPlutonDataSerializer::Deserialize( Azure::Core::Json::_internal::json::parse(response->GetBody())); } diff --git a/sdk/attestation/azure-security-attestation/src/private/attestation_deserializers_private.hpp b/sdk/attestation/azure-security-attestation/src/private/attestation_deserializers_private.hpp index ed30ac1dd7..661da14e7c 100644 --- a/sdk/attestation/azure-security-attestation/src/private/attestation_deserializers_private.hpp +++ b/sdk/attestation/azure-security-attestation/src/private/attestation_deserializers_private.hpp @@ -136,7 +136,7 @@ namespace Azure { namespace Security { namespace Attestation { namespace _detail Azure::Core::Json::_internal::json const& json); }; - struct TpmDataSerializer + struct TpmAndPlutonDataSerializer { static std::string Serialize(std::vector const& tpmData); static std::vector Deserialize(Azure::Core::Json::_internal::json const& jsonData); From 42c0a240efe2302d93eb6b882f020d16b7e0609b Mon Sep 17 00:00:00 2001 From: "Teresa Nguyen (from Dev Box)" Date: Tue, 21 Jul 2026 13:42:57 -0500 Subject: [PATCH 2/6] refactoring --- .../azure/attestation/attestation_client.hpp | 7 ++++ .../src/attestation_client.cpp | 42 +++++++------------ 2 files changed, 23 insertions(+), 26 deletions(-) diff --git a/sdk/attestation/azure-security-attestation/inc/azure/attestation/attestation_client.hpp b/sdk/attestation/azure-security-attestation/inc/azure/attestation/attestation_client.hpp index ede265e3c0..009f520f52 100644 --- a/sdk/attestation/azure-security-attestation/inc/azure/attestation/attestation_client.hpp +++ b/sdk/attestation/azure-security-attestation/inc/azure/attestation/attestation_client.hpp @@ -262,6 +262,13 @@ namespace Azure { namespace Security { namespace Attestation { Azure::Core::Context const& context = Azure::Core::Context{}) const; private: + template + Response AttestBackend( + std::vector const& dataToAttest, + std::string const& tracingName, + std::string const& attestPath, + Azure::Core::Context const& context) const; + Azure::Core::Url m_endpoint; std::string m_apiVersion; std::shared_ptr m_pipeline; diff --git a/sdk/attestation/azure-security-attestation/src/attestation_client.cpp b/sdk/attestation/azure-security-attestation/src/attestation_client.cpp index f5ba83627f..0c996b93f8 100644 --- a/sdk/attestation/azure-security-attestation/src/attestation_client.cpp +++ b/sdk/attestation/azure-security-attestation/src/attestation_client.cpp @@ -203,12 +203,14 @@ Azure::Response> AttestationClient::AttestOp } } -Azure::Response AttestationClient::AttestTpm( +template +Azure::Response AttestationClient::AttestBackend( std::vector const& dataToAttest, - AttestTpmOptions const&, + std::string const& tracingName, + std::string const& attestPath, Azure::Core::Context const& context) const { - auto tracingContext(m_tracingFactory.CreateTracingContext("AttestTpm", context)); + auto tracingContext(m_tracingFactory.CreateTracingContext(tracingName, context)); try { std::string jsonToSend = TpmAndPlutonDataSerializer::Serialize(dataToAttest); @@ -216,13 +218,13 @@ Azure::Response AttestationClient::AttestTpm( Azure::Core::IO::MemoryBodyStream stream(encodedVector); auto request = AttestationCommonRequest::CreateRequest( - m_endpoint, m_apiVersion, HttpMethod::Post, {"attest/Tpm"}, &stream); + m_endpoint, m_apiVersion, HttpMethod::Post, {attestPath}, &stream); // Send the request to the service. auto response = AttestationCommonRequest::SendRequest(*m_pipeline, request, tracingContext.Context); std::vector returnedBody{TpmAndPlutonDataSerializer::Deserialize(response)}; - return Response(TpmAttestationResult{returnedBody}, std::move(response)); + return Response(ResultT{returnedBody}, std::move(response)); } catch (std::runtime_error const& ex) { @@ -231,32 +233,20 @@ Azure::Response AttestationClient::AttestTpm( } } +Azure::Response AttestationClient::AttestTpm( + std::vector const& dataToAttest, + AttestTpmOptions const&, + Azure::Core::Context const& context) const +{ + return AttestBackend(dataToAttest, "AttestTpm", "attest/Tpm", context); +} + Azure::Response AttestationClient::AttestPluton( std::vector const& dataToAttest, AttestPlutonOptions const&, Azure::Core::Context const& context) const { - auto tracingContext(m_tracingFactory.CreateTracingContext("AttestPluton", context)); - try - { - std::string jsonToSend = TpmAndPlutonDataSerializer::Serialize(dataToAttest); - auto encodedVector = std::vector(jsonToSend.begin(), jsonToSend.end()); - Azure::Core::IO::MemoryBodyStream stream(encodedVector); - - auto request = AttestationCommonRequest::CreateRequest( - m_endpoint, m_apiVersion, HttpMethod::Post, {"attest/Pluton"}, &stream); - - // Send the request to the service. - auto response - = AttestationCommonRequest::SendRequest(*m_pipeline, request, tracingContext.Context); - std::vector returnedBody{TpmAndPlutonDataSerializer::Deserialize(response)}; - return Response(PlutonAttestationResult{returnedBody}, std::move(response)); - } - catch (std::runtime_error const& ex) - { - tracingContext.Span.AddEvent(ex); - throw; - } + return AttestBackend(dataToAttest, "AttestPluton", "attest/Pluton", context); } namespace { From c09adea5eff2ffc9e69ecaca997020de0ef934fb Mon Sep 17 00:00:00 2001 From: "Teresa Nguyen (from Dev Box)" Date: Wed, 29 Jul 2026 11:49:56 -0500 Subject: [PATCH 3/6] add pluton test, parameter renaming --- .../azure-security-attestation/CHANGELOG.md | 4 + .../src/attestation_client.cpp | 4 +- .../attestation_deserializers_private.cpp | 4 +- .../attestation_deserializers_private.hpp | 2 +- .../test/ut/CMakeLists.txt | 3 +- .../test/ut/plutonattestation_test.cpp | 145 ++++++++++++++++++ 6 files changed, 156 insertions(+), 6 deletions(-) create mode 100644 sdk/attestation/azure-security-attestation/test/ut/plutonattestation_test.cpp diff --git a/sdk/attestation/azure-security-attestation/CHANGELOG.md b/sdk/attestation/azure-security-attestation/CHANGELOG.md index cf63e5680f..e65bb046e4 100644 --- a/sdk/attestation/azure-security-attestation/CHANGELOG.md +++ b/sdk/attestation/azure-security-attestation/CHANGELOG.md @@ -4,6 +4,10 @@ ### Features Added +- Added `AttestationClient::AttestPluton` method for Pluton attestation support. +- Added `PlutonAttestationResult` model type containing the attestation response data. +- Added `AttestPlutonOptions` options type for configuring Pluton attestation requests. + ### Breaking Changes ### Bugs Fixed diff --git a/sdk/attestation/azure-security-attestation/src/attestation_client.cpp b/sdk/attestation/azure-security-attestation/src/attestation_client.cpp index 0c996b93f8..5f34dd59e9 100644 --- a/sdk/attestation/azure-security-attestation/src/attestation_client.cpp +++ b/sdk/attestation/azure-security-attestation/src/attestation_client.cpp @@ -223,8 +223,8 @@ Azure::Response AttestationClient::AttestBackend( // Send the request to the service. auto response = AttestationCommonRequest::SendRequest(*m_pipeline, request, tracingContext.Context); - std::vector returnedBody{TpmAndPlutonDataSerializer::Deserialize(response)}; - return Response(ResultT{returnedBody}, std::move(response)); + auto returnedBody = TpmAndPlutonDataSerializer::Deserialize(response); + return Response(ResultT{std::move(returnedBody)}, std::move(response)); } catch (std::runtime_error const& ex) { diff --git a/sdk/attestation/azure-security-attestation/src/private/attestation_deserializers_private.cpp b/sdk/attestation/azure-security-attestation/src/private/attestation_deserializers_private.cpp index 48885bf7e4..86d529ebfa 100644 --- a/sdk/attestation/azure-security-attestation/src/private/attestation_deserializers_private.cpp +++ b/sdk/attestation/azure-security-attestation/src/private/attestation_deserializers_private.cpp @@ -396,10 +396,10 @@ namespace Azure { namespace Security { namespace Attestation { namespace _detail returnValue.CertificateThumbprint, jsonResult, "x-ms-certificate-thumbprint"); return returnValue; } - std::string TpmAndPlutonDataSerializer::Serialize(std::vector const& tpmData) + std::string TpmAndPlutonDataSerializer::Serialize(std::vector const& data) { Azure::Core::Json::_internal::json jsonData; - jsonData["data"] = Azure::Core::_internal::Base64Url::Base64UrlEncode(tpmData); + jsonData["data"] = Azure::Core::_internal::Base64Url::Base64UrlEncode(data); return jsonData.dump(); } std::vector TpmAndPlutonDataSerializer::Deserialize( diff --git a/sdk/attestation/azure-security-attestation/src/private/attestation_deserializers_private.hpp b/sdk/attestation/azure-security-attestation/src/private/attestation_deserializers_private.hpp index 661da14e7c..8b407257da 100644 --- a/sdk/attestation/azure-security-attestation/src/private/attestation_deserializers_private.hpp +++ b/sdk/attestation/azure-security-attestation/src/private/attestation_deserializers_private.hpp @@ -138,7 +138,7 @@ namespace Azure { namespace Security { namespace Attestation { namespace _detail struct TpmAndPlutonDataSerializer { - static std::string Serialize(std::vector const& tpmData); + static std::string Serialize(std::vector const& data); static std::vector Deserialize(Azure::Core::Json::_internal::json const& jsonData); static std::vector Deserialize( std::unique_ptr const& response); diff --git a/sdk/attestation/azure-security-attestation/test/ut/CMakeLists.txt b/sdk/attestation/azure-security-attestation/test/ut/CMakeLists.txt index b81ec3d389..8fb5220fc9 100644 --- a/sdk/attestation/azure-security-attestation/test/ut/CMakeLists.txt +++ b/sdk/attestation/azure-security-attestation/test/ut/CMakeLists.txt @@ -1,7 +1,7 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. -#cspell: words tpmattestation +#cspell: words tpmattestation plutonattestation cmake_minimum_required (VERSION 3.13) project (azure-security-attestation-test LANGUAGES CXX) @@ -35,6 +35,7 @@ add_executable ( policygetset_test.cpp token_test.cpp tpmattestation_test.cpp + plutonattestation_test.cpp ) target_compile_definitions(azure-security-attestation-test PRIVATE _azure_BUILDING_TESTS) diff --git a/sdk/attestation/azure-security-attestation/test/ut/plutonattestation_test.cpp b/sdk/attestation/azure-security-attestation/test/ut/plutonattestation_test.cpp new file mode 100644 index 0000000000..828c020406 --- /dev/null +++ b/sdk/attestation/azure-security-attestation/test/ut/plutonattestation_test.cpp @@ -0,0 +1,145 @@ +// 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 +#include +#include + +#include + +#include + +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 + class PlutonAttestationTests : public Azure::Core::Test::TestBase { + public: + PlutonAttestationTests() { TestBase::SetUpTestSuiteLocal(AZURE_TEST_ASSETS_DIR); }; + + protected: + std::shared_ptr m_credential; + std::unique_ptr 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( + CreateAdminClient(PlutonInstanceType::AAD)); + + // Set a minimal policy for Pluton attestation. + m_adminClient->SetAttestationPolicy( + AttestationType::Tpm, + "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::Tpm); + } + } + + // 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(); + 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(); + 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(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 From 68aa9f0bb018f2412469a706d9eb78966af3515b Mon Sep 17 00:00:00 2001 From: "Teresa Nguyen (from Dev Box)" Date: Thu, 6 Aug 2026 10:36:19 -0500 Subject: [PATCH 4/6] policy tests --- .../azure/attestation/attestation_client_models.hpp | 6 ++++++ .../src/attestation_administration_client.cpp | 1 + .../test/ut/plutonattestation_test.cpp | 8 ++++++-- .../test/ut/policygetset_test.cpp | 12 ++++++++---- 4 files changed, 21 insertions(+), 6 deletions(-) diff --git a/sdk/attestation/azure-security-attestation/inc/azure/attestation/attestation_client_models.hpp b/sdk/attestation/azure-security-attestation/inc/azure/attestation/attestation_client_models.hpp index 9fd39507ca..72c7b01705 100644 --- a/sdk/attestation/azure-security-attestation/inc/azure/attestation/attestation_client_models.hpp +++ b/sdk/attestation/azure-security-attestation/inc/azure/attestation/attestation_client_models.hpp @@ -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; }; /** diff --git a/sdk/attestation/azure-security-attestation/src/attestation_administration_client.cpp b/sdk/attestation/azure-security-attestation/src/attestation_administration_client.cpp index 1eec808ad9..d8c31e731f 100644 --- a/sdk/attestation/azure-security-attestation/src/attestation_administration_client.cpp +++ b/sdk/attestation/azure-security-attestation/src/attestation_administration_client.cpp @@ -32,6 +32,7 @@ using namespace Azure::Core::_internal; const Models::AttestationType AttestationType::SgxEnclave("SgxEnclave"); const Models::AttestationType AttestationType::OpenEnclave("OpenEnclave"); const Models::AttestationType AttestationType::Tpm("Tpm"); +const Models::AttestationType AttestationType::Pluton("Pluton"); const Models::PolicyModification PolicyModification::Removed("Removed"); const Models::PolicyModification PolicyModification::Updated("Updated"); const Models::PolicyCertificateModification PolicyCertificateModification::IsAbsent("IsAbsent"); diff --git a/sdk/attestation/azure-security-attestation/test/ut/plutonattestation_test.cpp b/sdk/attestation/azure-security-attestation/test/ut/plutonattestation_test.cpp index 828c020406..653c159ba4 100644 --- a/sdk/attestation/azure-security-attestation/test/ut/plutonattestation_test.cpp +++ b/sdk/attestation/azure-security-attestation/test/ut/plutonattestation_test.cpp @@ -27,6 +27,8 @@ namespace Azure { namespace Security { namespace Attestation { namespace Test { }; // cspell: words plutonattestation + static const std::string PlutonApiVersion = "2026-03-11preview"; + class PlutonAttestationTests : public Azure::Core::Test::TestBase { public: PlutonAttestationTests() { TestBase::SetUpTestSuiteLocal(AZURE_TEST_ASSETS_DIR); }; @@ -47,7 +49,7 @@ namespace Azure { namespace Security { namespace Attestation { namespace Test { // Set a minimal policy for Pluton attestation. m_adminClient->SetAttestationPolicy( - AttestationType::Tpm, + AttestationType::Pluton, "version=1.0; authorizationrules{=> permit();}; issuancerules{};"); } } @@ -59,7 +61,7 @@ namespace Azure { namespace Security { namespace Attestation { namespace Test { { if (m_adminClient) { - m_adminClient->ResetAttestationPolicy(AttestationType::Tpm); + m_adminClient->ResetAttestationPolicy(AttestationType::Pluton); } } @@ -105,6 +107,7 @@ namespace Azure { namespace Security { namespace Attestation { namespace Test { { // `InitClientOptions` takes care of setting up Record&Playback. AttestationClientOptions options = InitClientOptions(); + options.ApiVersion = PlutonApiVersion; options.TokenValidationOptions = GetTokenValidationOptions(); auto credential = GetTestCredential(); return AttestationClient::Create(GetInstanceUri(instanceType), credential, options); @@ -115,6 +118,7 @@ namespace Azure { namespace Security { namespace Attestation { namespace Test { // `InitTestClient` takes care of setting up Record&Playback. AttestationAdministrationClientOptions options = InitClientOptions(); + options.ApiVersion = PlutonApiVersion; options.TokenValidationOptions = GetTokenValidationOptions(); auto credential = GetTestCredential(); return AttestationAdministrationClient::Create( diff --git a/sdk/attestation/azure-security-attestation/test/ut/policygetset_test.cpp b/sdk/attestation/azure-security-attestation/test/ut/policygetset_test.cpp index 562f8f8931..8af74215e0 100644 --- a/sdk/attestation/azure-security-attestation/test/ut/policygetset_test.cpp +++ b/sdk/attestation/azure-security-attestation/test/ut/policygetset_test.cpp @@ -207,8 +207,8 @@ namespace Azure { namespace Security { namespace Attestation { namespace Test { * that the returned policy makes sense as an attestation policy (starts with the text "version" * - beyond that, we can't verify the response). * - * Note that VSM/VBS/TPM policies can be empty, so if we encounter an empty policy, verify that - * the policy came from TPM attestation. + * Note that VSM/VBS/TPM and Pluton policies can be empty, so if we encounter an empty policy, verify that + * the policy came from TPM or Pluton attestation. * * One additional check is performed in live mode: We verify that the issuer of the returned * attestation token matches the endpoint. This check cannot be run against recorded collateral @@ -228,7 +228,8 @@ namespace Azure { namespace Security { namespace Attestation { namespace Test { // documents. if (policy.Value.Body.empty()) { - EXPECT_EQ(AttestationType::Tpm, attestationType); + EXPECT_TRUE( + AttestationType::Tpm == attestationType || AttestationType::Pluton == attestationType); } else { @@ -342,7 +343,10 @@ namespace Azure { namespace Security { namespace Attestation { namespace Test { for (auto const& value : typeNameList) { for (auto const& type : - {AttestationType::SgxEnclave, AttestationType::OpenEnclave, AttestationType::Tpm}) + {AttestationType::SgxEnclave, + AttestationType::OpenEnclave, + AttestationType::Tpm, + AttestationType::Pluton}) { returnCases.emplace_back(PolicyTestParam{testCaseType, value, type}); } From 17600310c87cb7063c39b5977208e556e718b4b1 Mon Sep 17 00:00:00 2001 From: "Teresa Nguyen (from Dev Box)" Date: Thu, 6 Aug 2026 11:12:59 -0500 Subject: [PATCH 5/6] api version update --- .../test/ut/plutonattestation_test.cpp | 2 +- .../azure-security-attestation/test/ut/policygetset_test.cpp | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/sdk/attestation/azure-security-attestation/test/ut/plutonattestation_test.cpp b/sdk/attestation/azure-security-attestation/test/ut/plutonattestation_test.cpp index 653c159ba4..8c1e62a225 100644 --- a/sdk/attestation/azure-security-attestation/test/ut/plutonattestation_test.cpp +++ b/sdk/attestation/azure-security-attestation/test/ut/plutonattestation_test.cpp @@ -27,7 +27,7 @@ namespace Azure { namespace Security { namespace Attestation { namespace Test { }; // cspell: words plutonattestation - static const std::string PlutonApiVersion = "2026-03-11preview"; + static const std::string PlutonApiVersion = "2026-03-11-preview"; class PlutonAttestationTests : public Azure::Core::Test::TestBase { public: diff --git a/sdk/attestation/azure-security-attestation/test/ut/policygetset_test.cpp b/sdk/attestation/azure-security-attestation/test/ut/policygetset_test.cpp index 8af74215e0..9ff3117abe 100644 --- a/sdk/attestation/azure-security-attestation/test/ut/policygetset_test.cpp +++ b/sdk/attestation/azure-security-attestation/test/ut/policygetset_test.cpp @@ -94,6 +94,11 @@ namespace Azure { namespace Security { namespace Attestation { namespace Test { = InitClientOptions(); options.TokenValidationOptions = GetTokenValidationOptions(); + if (GetParam().TeeType == AttestationType::Pluton) + { + options.ApiVersion = "2026-03-11-preview"; + } + auto credential = GetTestCredential(); return AttestationAdministrationClient::Create(m_endpoint, credential, options); From 45422ccf858a9f1c3f3f856bac39c37f300e84d4 Mon Sep 17 00:00:00 2001 From: "Teresa Nguyen (from Dev Box)" Date: Fri, 7 Aug 2026 16:15:00 -0500 Subject: [PATCH 6/6] api version notes --- sdk/attestation/azure-security-attestation/CHANGELOG.md | 3 ++- .../inc/azure/attestation/attestation_client.hpp | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/sdk/attestation/azure-security-attestation/CHANGELOG.md b/sdk/attestation/azure-security-attestation/CHANGELOG.md index e65bb046e4..ec7a0f3647 100644 --- a/sdk/attestation/azure-security-attestation/CHANGELOG.md +++ b/sdk/attestation/azure-security-attestation/CHANGELOG.md @@ -4,7 +4,8 @@ ### Features Added -- Added `AttestationClient::AttestPluton` method for Pluton attestation support. +- Added `AttestationClient::AttestPluton` method for Pluton attestation support. + Pluton attestation is not currently supported for the default API version and can only be accessed via 2026-03-11-preview. - Added `PlutonAttestationResult` model type containing the attestation response data. - Added `AttestPlutonOptions` options type for configuring Pluton attestation requests. diff --git a/sdk/attestation/azure-security-attestation/inc/azure/attestation/attestation_client.hpp b/sdk/attestation/azure-security-attestation/inc/azure/attestation/attestation_client.hpp index 009f520f52..1ac9817bad 100644 --- a/sdk/attestation/azure-security-attestation/inc/azure/attestation/attestation_client.hpp +++ b/sdk/attestation/azure-security-attestation/inc/azure/attestation/attestation_client.hpp @@ -249,6 +249,7 @@ namespace Azure { namespace Security { namespace Attestation { /** * @brief Sends Pluton-based attestation data to the service. + * Pluton attestation is not currently supported for the default API version and can only be accessed via 2026-03-11-preview. * * @param dataToAttest - Attestation request data. * @param options - Options to the attestation request.