diff --git a/sdk/keyvault/azure-security-keyvault-keys/CMakeLists.txt b/sdk/keyvault/azure-security-keyvault-keys/CMakeLists.txt index afc771f665..ddd0bbdaed 100644 --- a/sdk/keyvault/azure-security-keyvault-keys/CMakeLists.txt +++ b/sdk/keyvault/azure-security-keyvault-keys/CMakeLists.txt @@ -100,6 +100,8 @@ set( src/keyvault_key.cpp src/keyvault_protocol.cpp src/recover_deleted_key_operation.cpp + src/key_rotation_policy.cpp + src/key_get_random_bytes.cpp ) add_library(azure-security-keyvault-keys diff --git a/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/key_client.hpp b/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/key_client.hpp index beeed624b7..b68c7d3c89 100644 --- a/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/key_client.hpp +++ b/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/key_client.hpp @@ -190,6 +190,7 @@ namespace Azure { namespace Security { namespace KeyVault { namespace Keys { * @param options The #GetPropertiesOfKeysOptions object to for setting the operation * up. * @param context A #Azure::Core::Context controlling the request lifetime. + * @return KeyPropertiesPagedResponse */ KeyPropertiesPagedResponse GetPropertiesOfKeys( GetPropertiesOfKeysOptions const& options = GetPropertiesOfKeysOptions(), @@ -212,6 +213,7 @@ namespace Azure { namespace Security { namespace KeyVault { namespace Keys { * @param options The #GetPropertiesOfKeyVersionsOptions object to for setting the * operation up. * @param context A #Azure::Core::Context controlling the request lifetime. + * @return KeyPropertiesPagedResponse */ KeyPropertiesPagedResponse GetPropertiesOfKeyVersions( std::string const& name, @@ -283,6 +285,7 @@ namespace Azure { namespace Security { namespace KeyVault { namespace Keys { * * @param name The name of the key. * @param context A #Azure::Core::Context controlling the request lifetime. + * @return Azure::Response */ Azure::Response PurgeDeletedKey( std::string const& name, @@ -342,6 +345,7 @@ namespace Azure { namespace Security { namespace KeyVault { namespace Keys { * * @param name The name of the key. * @param context A #Azure::Core::Context controlling the request lifetime. + * @return Azure::Response */ Azure::Response BackupKey( std::string const& name, @@ -364,6 +368,7 @@ namespace Azure { namespace Security { namespace KeyVault { namespace Keys { * * @param backup The backup blob associated with a key. * @param context A #Azure::Core::Context controlling the request lifetime. + * @return Azure::Response */ Azure::Response RestoreKeyBackup( std::vector const& backup, @@ -398,11 +403,55 @@ namespace Azure { namespace Security { namespace KeyVault { namespace Keys { * @param importKeyOptions The key import configuration object containing information about * the #JsonWebKey being imported. * @param context A #Azure::Core::Context controlling the request lifetime. + * @return Azure::Response */ Azure::Response ImportKey( ImportKeyOptions const& importKeyOptions, Azure::Core::Context const& context = Azure::Core::Context()) const; + /** + * @brief Lists the policy for a key. + * + * @remark The GetKeyRotationPolicy operation returns the specified key policy resources in the + * specified key vault. This operation requires the keys/get permission. + * + * @param name The name of the key in a given key vault. + * @param context A #Azure::Core::Context controlling the request lifetime. + * @return Azure::Response + */ + Azure::Response GetKeyRotationPolicy( + std::string const& name, + Azure::Core::Context const& context = Azure::Core::Context()) const; + + /** + * @brief Updates the rotation policy for a key. + * + * @remark Set specified members in the key policy. Leave others as undefined. This operation + * requires the keys/update permission. + * + * @param name The name of the key in a given key vault. + * @param rotationPolicy The policy for the key. + * @param context A #Azure::Core::Context controlling the request lifetime. + * @return Azure::Response + */ + Azure::Response PutKeyRotationPolicy( + std::string const& name, + KeyRotationPolicy const& rotationPolicy, + Azure::Core::Context const& context = Azure::Core::Context()) const; + + /** + * @brief Get the requested number of bytes containing random values. + * + * @remark Get the requested number of bytes containing random values from a managed HSM. + * + * @param options The request object to get random bytes. + * @param context A #Azure::Core::Context controlling the request lifetime. + * @return Azure::Response> + */ + Azure::Response GetRandomBytes( + GetRandomBytesOptions const& options, + Azure::Core::Context const& context = Azure::Core::Context()) const; + /** * @brief Gets the key client's primary URL endpoint. * diff --git a/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/key_client_models.hpp b/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/key_client_models.hpp index 825346f8a9..8542eff623 100644 --- a/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/key_client_models.hpp +++ b/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/key_client_models.hpp @@ -120,8 +120,8 @@ namespace Azure { namespace Security { namespace KeyVault { namespace Keys { AZ_SECURITY_KEYVAULT_KEYS_DLLEXPORT static const KeyOperation WrapKey; /** - * @brief The key can be used to unwrap another key with the UnwrapKey(KeyWrapAlgorithm, Byte[], - * CancellationToken) method. + * @brief The key can be used to unwrap another key with the UnwrapKey(KeyWrapAlgorithm, + * Byte[], CancellationToken) method. */ AZ_SECURITY_KEYVAULT_KEYS_DLLEXPORT static const KeyOperation UnwrapKey; @@ -420,8 +420,8 @@ namespace Azure { namespace Security { namespace KeyVault { namespace Keys { std::string Version; /** - * @brief Indicate whether the key's lifetime is managed by Key Vault. If this key is backing a - * Key Vault certificate, the value will be true. + * @brief Indicate whether the key's lifetime is managed by Key Vault. If this key is backing + * a Key Vault certificate, the value will be true. * */ bool Managed = false; @@ -505,7 +505,8 @@ namespace Azure { namespace Security { namespace KeyVault { namespace Keys { virtual ~KeyVaultKey() = default; /** - * @brief The cryptographic key, the key type, and the operations you can perform using the key. + * @brief The cryptographic key, the key type, and the operations you can perform using the + * key. * */ JsonWebKey Key; @@ -670,7 +671,8 @@ namespace Azure { namespace Security { namespace KeyVault { namespace Keys { * * @param deletedKeyProperties A previously created #DeletedKeyPagedResponse that is used to * init this new instance. - * @param rawResponse The HTTP raw response from where the #DeletedKeyPagedResponse was parsed. + * @param rawResponse The HTTP raw response from where the #DeletedKeyPagedResponse was + * parsed. * @param keyClient A key client required for getting the next pages. */ DeletedKeyPagedResponse( @@ -713,8 +715,8 @@ namespace Azure { namespace Security { namespace KeyVault { namespace Keys { std::string m_continuationToken; /* This is the implementation for checking the status of a deleted key. The key is considered - * deleted if querying /deletedkeys/keyName returns 200 from server. Or whenever soft-delete is - * disabled.*/ + * deleted if querying /deletedkeys/keyName returns 200 from server. Or whenever soft-delete + * is disabled.*/ std::unique_ptr PollInternal( Azure::Core::Context const& context) override; @@ -776,7 +778,8 @@ namespace Azure { namespace Security { namespace KeyVault { namespace Keys { Azure::Security::KeyVault::Keys::DeletedKey Value() const override { return m_value; } /** - * @brief Get an Url as string which can be used to get the status of the delete key operation. + * @brief Get an Url as string which can be used to get the status of the delete key + * operation. * * @return std::string */ @@ -789,7 +792,8 @@ namespace Azure { namespace Security { namespace KeyVault { namespace Keys { * @remark After the operation is initialized, it is used to poll the last update from the * server using the \p context. * - * @param resumeToken A previously generated token used to resume the polling of the operation. + * @param resumeToken A previously generated token used to resume the polling of the + * operation. * @param client A #KeyClient that is used for getting status updates. * @param context A #Azure::Core::Context controlling the request lifetime. * @return DeleteKeyOperation @@ -837,8 +841,8 @@ namespace Azure { namespace Security { namespace KeyVault { namespace Keys { } /* - * Only friend classes are permitted to construct a RecoverDeletedKeyOperation. This is because - * a KeyVaultPipelne is required and it is not exposed to customers. + * Only friend classes are permitted to construct a RecoverDeletedKeyOperation. This is + * because a KeyVaultPipelne is required and it is not exposed to customers. * * Since C++ doesn't offer `internal` access, we use friends-only instead. */ @@ -875,7 +879,8 @@ namespace Azure { namespace Security { namespace KeyVault { namespace Keys { Azure::Security::KeyVault::Keys::KeyVaultKey Value() const override { return m_value; } /** - * @brief Get an Url as string which can be used to get the status of the delete key operation. + * @brief Get an Url as string which can be used to get the status of the delete key + * operation. * * @return std::string */ @@ -888,7 +893,8 @@ namespace Azure { namespace Security { namespace KeyVault { namespace Keys { * @remark After the operation is initialized, it is used to poll the last update from the * server using the \p context. * - * @param resumeToken A previously generated token used to resume the polling of the operation. + * @param resumeToken A previously generated token used to resume the polling of the + * operation. * @param client A #KeyClient that is used for getting status updates. * @param context A #Azure::Core::Context controlling the request lifetime. * @return DeleteKeyOperation @@ -899,4 +905,124 @@ namespace Azure { namespace Security { namespace KeyVault { namespace Keys { Azure::Core::Context const& context = Azure::Core::Context()); }; + /** + * @brief LifeTime action type + * + */ + enum class LifetimeActionType + { + /** + * @brief Rotate the key based on the key policy. + * + */ + Rotate, + + /** + * @brief Trigger event grid events. For preview, the notification time is not configurable + * and it is default to 30 days before expiry. + * + */ + Notify + }; + + /** + * @brief A condition to be satisfied for an action to be executed. + */ + struct LifetimeActionsTrigger final + { + /** + * @brief Time after creation to attempt to rotate. It only applies to rotate. It will be in + * ISO 8601 duration format. Example: 90 days : "P90D" + * + */ + Azure::Nullable TimeAfterCreate; + /** + * @brief Time before expiry to attempt to rotate or notify. It will be in ISO 8601 duration + * format. Example: 90 days : "P90D" + * + */ + Azure::Nullable TimeBeforeExpiry; + }; + + /** + * @brief Action and its trigger that will be performed by Key Vault over the lifetime of a key. + * + */ + struct LifetimeActionsType final + { + /** + * @brief The condition that will execute the action. + * + */ + LifetimeActionsTrigger Trigger; + + /** + * @brief The action that will be executed. + */ + LifetimeActionType Action; + }; + + /** + * @brief The key rotation policy attributes. + * + */ + struct KeyRotationPolicyAttributes final + { + /** + * @brief The expiryTime will be applied on the new key version. It should be at least 28 + * days. It will be in ISO 8601 Format. Examples: 90 days: P90D, 3 months: P3M, 48 hours: + * PT48H, 1 year and 10 days: P1Y10D + */ + Azure::Nullable ExpiryTime; + + /** + * @brief The key rotation policy created time in UTC. + * + */ + Azure::Nullable Created; + + /** + * @brief The key rotation policy's last updated time in UTC. + * + */ + Azure::Nullable Updated; + }; + + /** + * @brief Rotation policy for a key. + */ + struct KeyRotationPolicy final + { + /** + * @brief The key policy id. + */ + std::string Id; + + /** + * @brief Actions that will be performed by Key Vault over the lifetime of a key. For preview, + * lifetimeActions can only have two items at maximum: one for rotate, one for notify. + * Notification time would be default to 30 days before expiry and it is not configurable. + * + */ + std::vector LifetimeActions; + + /** + * @brief The key rotation policy attributes. + */ + KeyRotationPolicyAttributes Attributes; + }; + + /** + * @brief The GetRandomBytes result type containing the random bytes bytes. + * + */ + struct GetRandomBytesResult final + { + /** + * @brief The random generated bytes. + * + */ + std::vector RandomBytes; + }; + }}}} // namespace Azure::Security::KeyVault::Keys diff --git a/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/key_client_options.hpp b/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/key_client_options.hpp index 63f0bf6fd0..522f231f2a 100644 --- a/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/key_client_options.hpp +++ b/sdk/keyvault/azure-security-keyvault-keys/inc/azure/keyvault/keys/key_client_options.hpp @@ -401,4 +401,17 @@ namespace Azure { namespace Security { namespace KeyVault { namespace Keys { std::string const& Name() const { return Properties.Name; } }; + /** + * @brief Get Random Bytes options + * + */ + struct GetRandomBytesOptions final + { + /** + * @brief The requested number of random bytes. + * + */ + int32_t Count; + }; + }}}} // namespace Azure::Security::KeyVault::Keys diff --git a/sdk/keyvault/azure-security-keyvault-keys/src/key_client.cpp b/sdk/keyvault/azure-security-keyvault-keys/src/key_client.cpp index a1a6df3325..ca6621390c 100644 --- a/sdk/keyvault/azure-security-keyvault-keys/src/key_client.cpp +++ b/sdk/keyvault/azure-security-keyvault-keys/src/key_client.cpp @@ -381,6 +381,60 @@ Azure::Response KeyClient::ImportKey( return Azure::Response(std::move(value), std::move(rawResponse)); } +Azure::Response KeyClient::GetKeyRotationPolicy( + std::string const& name, + Azure::Core::Context const& context) const +{ + // Request with no payload + auto request + = CreateRequest(HttpMethod::Get, {_detail::KeysPath, name, _detail::RotationPolicyPath}); + request.SetHeader(HttpShared::ContentType, HttpShared::ApplicationJson); + // Send and parse respone + auto rawResponse = SendRequest(request, context); + auto value = _detail::KeyRotationPolicySerializer::KeyRotationPolicyDeserialize(*rawResponse); + return Azure::Response(std::move(value), std::move(rawResponse)); +} + +Azure::Response KeyClient::PutKeyRotationPolicy( + std::string const& name, + KeyRotationPolicy const& rotationPolicy, + Azure::Core::Context const& context) const +{ + // Payload for the request + auto payload = _detail::KeyRotationPolicySerializer::KeyRotationPolicySerialize(rotationPolicy); + Azure::Core::IO::MemoryBodyStream payloadStream( + reinterpret_cast(payload.data()), payload.size()); + + // Request and settings + auto request = CreateRequest( + HttpMethod::Put, {_detail::KeysPath, name, _detail::RotationPolicyPath}, &payloadStream); + request.SetHeader(HttpShared::ContentType, HttpShared::ApplicationJson); + + // Send and parse respone + auto rawResponse = SendRequest(request, context); + auto value = _detail::KeyRotationPolicySerializer::KeyRotationPolicyDeserialize(*rawResponse); + return Azure::Response(std::move(value), std::move(rawResponse)); +} + +Azure::Response KeyClient::GetRandomBytes( + GetRandomBytesOptions const& options, + Azure::Core::Context const& context) const +{ + auto payload = _detail::GetRandomBytesSerializer::GetRandomBytesOptionsSerialize(options); + Azure::Core::IO::MemoryBodyStream payloadStream( + reinterpret_cast(payload.data()), payload.size()); + + // Request and settings + auto request = CreateRequest(HttpMethod::Post, {"/rng"}, &payloadStream); + request.SetHeader(HttpShared::ContentType, HttpShared::ApplicationJson); + + // Send and parse respone + auto rawResponse = SendRequest(request, context); + auto response = GetRandomBytesResult{ + _detail::GetRandomBytesSerializer::GetRandomBytesResponseDeserialize(*rawResponse)}; + return Azure::Response(std::move(response), std::move(rawResponse)); +} + Cryptography::CryptographyClient KeyClient::GetCryptographyClient( std::string const& name, std::string const& version) const diff --git a/sdk/keyvault/azure-security-keyvault-keys/src/key_get_random_bytes.cpp b/sdk/keyvault/azure-security-keyvault-keys/src/key_get_random_bytes.cpp new file mode 100644 index 0000000000..7e5c9746ff --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-keys/src/key_get_random_bytes.cpp @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +#include "azure/keyvault/keys/key_client_models.hpp" +#include "private/key_constants.hpp" +#include "private/key_serializers.hpp" +#include +#include +#include + +using namespace Azure::Security::KeyVault::Keys; +using namespace Azure::Core::Json::_internal; + +std::string _detail::GetRandomBytesSerializer::GetRandomBytesOptionsSerialize( + GetRandomBytesOptions const& options) +{ + json payload; + + payload[_detail::CountPropertiesValue] = options.Count; + + return payload.dump(); +} + +std::vector _detail::GetRandomBytesSerializer::GetRandomBytesResponseDeserialize( + Azure::Core::Http::RawResponse const& rawResponse) +{ + auto const& body = rawResponse.GetBody(); + auto const jsonParser = Azure::Core::Json::_internal::json::parse(body); + + auto value = jsonParser[_detail::ValueParameterValue].get(); + return Azure::Core::_internal::Base64Url::Base64UrlDecode(value); +} diff --git a/sdk/keyvault/azure-security-keyvault-keys/src/key_rotation_policy.cpp b/sdk/keyvault/azure-security-keyvault-keys/src/key_rotation_policy.cpp new file mode 100644 index 0000000000..6b27ce1c72 --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-keys/src/key_rotation_policy.cpp @@ -0,0 +1,126 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +#include +#include + +#include "azure/keyvault/keys/key_client_models.hpp" +#include "private/key_constants.hpp" +#include "private/key_serializers.hpp" + +using namespace Azure::Security::KeyVault::Keys; +using namespace Azure::Core::Json::_internal; +using Azure::Core::_internal::PosixTimeConverter; + +KeyRotationPolicy _detail::KeyRotationPolicySerializer::KeyRotationPolicyDeserialize( + Azure::Core::Http::RawResponse const& rawResponse) +{ + return KeyRotationPolicyDeserialize(rawResponse.GetBody()); +} + +KeyRotationPolicy _detail::KeyRotationPolicySerializer::KeyRotationPolicyDeserialize( + std::vector const& body) +{ + auto const jsonParser = Azure::Core::Json::_internal::json::parse(body); + KeyRotationPolicy policy; + + policy.Id = jsonParser[_detail::IdValue].get(); + + if (!jsonParser[_detail::AttributesPropertyName].is_null()) + { + auto jsonFragment = jsonParser[_detail::AttributesPropertyName]; + policy.Attributes.ExpiryTime = jsonFragment[_detail::ExpiryTimeValue].get(); + + JsonOptional::SetIfExists( + policy.Attributes.Created, + jsonFragment, + _detail::CreatedPropertyName, + PosixTimeConverter::PosixTimeToDateTime); + + JsonOptional::SetIfExists( + policy.Attributes.Updated, + jsonFragment, + _detail::UpdatedPropertyName, + PosixTimeConverter::PosixTimeToDateTime); + } + + if (!jsonParser[_detail::LifeTimeActionsValue].is_null()) + { + auto lifeTimeActions = jsonParser[_detail::LifeTimeActionsValue]; + + for (auto action : lifeTimeActions) + { + LifetimeActionsType currentAction; + + JsonOptional::SetIfExists( + currentAction.Trigger.TimeAfterCreate, + action[_detail::TriggerActionsValue], + _detail::TACActionsValue); + + JsonOptional::SetIfExists( + currentAction.Trigger.TimeBeforeExpiry, + action[_detail::TriggerActionsValue], + _detail::TBEActionsValue); + + auto actionType = action[_detail::ActionActionsValue][TypeActionsValue].get(); + actionType = Azure::Core::_internal::StringExtensions::ToLower(actionType); + + if (actionType + == Azure::Core::_internal::StringExtensions::ToLower(_detail::RotateActionsValue)) + { + currentAction.Action = LifetimeActionType::Rotate; + } + else if ( + actionType + == Azure::Core::_internal::StringExtensions::ToLower(_detail::NotifyActionsValue)) + { + currentAction.Action = LifetimeActionType::Notify; + } + + policy.LifetimeActions.emplace_back(currentAction); + } + } + + return policy; +} + +std::string _detail::KeyRotationPolicySerializer::KeyRotationPolicySerialize( + KeyRotationPolicy const& rotationPolicy) +{ + json payload; + + JsonOptional::SetFromNullable( + rotationPolicy.Attributes.ExpiryTime, + payload[_detail::AttributesPropertyName], + _detail::ExpiryTimeValue); + + for (auto lifetimeAction : rotationPolicy.LifetimeActions) + { + json oneAction; + + JsonOptional::SetFromNullable( + lifetimeAction.Trigger.TimeAfterCreate, + oneAction[_detail::TriggerActionsValue], + _detail::TACActionsValue); + + JsonOptional::SetFromNullable( + lifetimeAction.Trigger.TimeBeforeExpiry, + oneAction[_detail::TriggerActionsValue], + _detail::TBEActionsValue); + + if (lifetimeAction.Action == LifetimeActionType::Notify) + { + oneAction[_detail::ActionActionsValue][_detail::TypeActionsValue] + = _detail::NotifyActionsValue; + } + else + { + oneAction[_detail::ActionActionsValue][_detail::TypeActionsValue] + = _detail::RotateActionsValue; + } + + payload[_detail::LifeTimeActionsValue].emplace_back(oneAction); + } + + return payload.dump(); +} diff --git a/sdk/keyvault/azure-security-keyvault-keys/src/private/key_constants.hpp b/sdk/keyvault/azure-security-keyvault-keys/src/private/key_constants.hpp index 1af3249a2e..715ccd26d2 100644 --- a/sdk/keyvault/azure-security-keyvault-keys/src/private/key_constants.hpp +++ b/sdk/keyvault/azure-security-keyvault-keys/src/private/key_constants.hpp @@ -126,4 +126,20 @@ namespace Azure { namespace Security { namespace KeyVault { namespace Keys { nam /***************** Service *********/ constexpr static const char ApiVersionValue[] = "api-version"; + /***************** Rotation Policy *********/ + constexpr static const char IdValue[] = "id"; + constexpr static const char ExpiryTimeValue[] = "expiryTime"; + constexpr static const char LifeTimeActionsValue[] = "lifetimeActions"; + constexpr static const char RotateActionsValue[] = "rotate"; + constexpr static const char NotifyActionsValue[] = "notify"; + constexpr static const char ActionActionsValue[] = "action"; + constexpr static const char TriggerActionsValue[] = "trigger"; + constexpr static const char TypeActionsValue[] = "type"; + constexpr static const char TBEActionsValue[] = "timeBeforeExpiry"; + constexpr static const char TACActionsValue[] = "timeAfterCreate"; + constexpr static const char RotationPolicyPath[] = "rotationpolicy"; + + /***************** Get Random Bytes *********/ + constexpr static const char CountPropertiesValue[] = "count"; + }}}}} // namespace Azure::Security::KeyVault::Keys::_detail diff --git a/sdk/keyvault/azure-security-keyvault-keys/src/private/key_serializers.hpp b/sdk/keyvault/azure-security-keyvault-keys/src/private/key_serializers.hpp index 5174f752de..30b613e2ea 100644 --- a/sdk/keyvault/azure-security-keyvault-keys/src/private/key_serializers.hpp +++ b/sdk/keyvault/azure-security-keyvault-keys/src/private/key_serializers.hpp @@ -115,4 +115,22 @@ namespace Azure { namespace Security { namespace KeyVault { namespace Keys { nam Azure::Core::Json::_internal::json const& jsonParser); }; + /**************** Key Properties ************/ + class KeyRotationPolicySerializer final { + public: + static KeyRotationPolicy KeyRotationPolicyDeserialize( + Azure::Core::Http::RawResponse const& rawResponse); + static KeyRotationPolicy KeyRotationPolicyDeserialize(std::vector const& body); + + static std::string KeyRotationPolicySerialize(KeyRotationPolicy const& rotationPolicy); + }; + + /**************** GetRandomBytes ************/ + class GetRandomBytesSerializer final { + public: + static std::string GetRandomBytesOptionsSerialize(GetRandomBytesOptions const& options); + static std::vector GetRandomBytesResponseDeserialize( + Azure::Core::Http::RawResponse const& rawResponse); + }; + }}}}} // namespace Azure::Security::KeyVault::Keys::_detail diff --git a/sdk/keyvault/azure-security-keyvault-keys/test/ut/CMakeLists.txt b/sdk/keyvault/azure-security-keyvault-keys/test/ut/CMakeLists.txt index 9e464f7aee..70665627d2 100644 --- a/sdk/keyvault/azure-security-keyvault-keys/test/ut/CMakeLists.txt +++ b/sdk/keyvault/azure-security-keyvault-keys/test/ut/CMakeLists.txt @@ -25,10 +25,10 @@ add_executable ( key_client_test.cpp key_client_update_test_live.cpp key_cryptographic_client_test_live.cpp + key_rotation_policy_test_live.cpp macro_guard.cpp mocked_client_test.cpp - mocked_transport_adapter_test.hpp -) + mocked_transport_adapter_test.hpp) create_per_service_target_build(keyvault azure-security-keyvault-keys-test) create_map_file(azure-security-keyvault-keys-test azure-security-keyvault-keys-test.map) diff --git a/sdk/keyvault/azure-security-keyvault-keys/test/ut/key_client_base_test.hpp b/sdk/keyvault/azure-security-keyvault-keys/test/ut/key_client_base_test.hpp index d7553a6f2f..59998d3668 100644 --- a/sdk/keyvault/azure-security-keyvault-keys/test/ut/key_client_base_test.hpp +++ b/sdk/keyvault/azure-security-keyvault-keys/test/ut/key_client_base_test.hpp @@ -71,13 +71,13 @@ namespace Azure { namespace Security { namespace KeyVault { namespace Keys { nam UpdateWaitingTime(m_testPollingIntervalMs); } - void CreateHsmClient() + void CreateHsmClient(std::string hsmUrl = "") { KeyClientOptions options; m_client = InitTestClient< Azure::Security::KeyVault::Keys::KeyClient, Azure::Security::KeyVault::Keys::KeyClientOptions>( - m_keyVaultHsmUrl, m_credential, options); + hsmUrl.length() == 0 ? m_keyVaultHsmUrl : hsmUrl, m_credential, options); } public: diff --git a/sdk/keyvault/azure-security-keyvault-keys/test/ut/key_rotation_policy_test_live.cpp b/sdk/keyvault/azure-security-keyvault-keys/test/ut/key_rotation_policy_test_live.cpp new file mode 100644 index 0000000000..ef4f053f01 --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-keys/test/ut/key_rotation_policy_test_live.cpp @@ -0,0 +1,164 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +#include "gtest/gtest.h" + +#include "key_client_base_test.hpp" +#include "private/key_serializers.hpp" +#include +#include +#include +#include + +#include + +using namespace Azure::Security::KeyVault::Keys::Test; +using namespace Azure; +using namespace Azure::Security::KeyVault::Keys; +using namespace Azure::Security::KeyVault::Keys::_detail; + +TEST_F(KeyVaultKeyClient, GetKeyRotationPolicy) +{ + auto const keyName = GetTestName(); + auto const& client = GetClientForTest(keyName); + + auto createKeyResponse = client.CreateEcKey(CreateEcKeyOptions(keyName)); + CheckValidResponse(createKeyResponse); + + std::string input + = "{\"id\":\"https://redacted.vault.azure.net/keys/GetKeyRotationPolicy/" + "rotationpolicy\",\"lifetimeActions\":[{\"trigger\":{\"timeAfterCreate\":\"P18M\"}," + "\"action\":{\"type\":\"Rotate\"}},{\"trigger\":{\"timeBeforeExpiry\":\"P30D\"},\"action\":" + "{\"type\":\"Notify\"}}],\"attributes\":{\"expiryTime\":\"P48M\",\"created\":1649797765," + "\"updated\":1649797765}}"; + + auto policy = KeyRotationPolicySerializer::KeyRotationPolicyDeserialize( + std::vector(input.begin(), input.end())); + + auto putPolicy = client.PutKeyRotationPolicy(keyName, policy).Value; + auto rotationPolicy = client.GetKeyRotationPolicy(keyName).Value; + + EXPECT_EQ(rotationPolicy.Attributes.ExpiryTime.Value(), policy.Attributes.ExpiryTime.Value()); + EXPECT_NE(rotationPolicy.Id.size(), size_t(0)); + EXPECT_EQ(rotationPolicy.LifetimeActions.size(), policy.LifetimeActions.size()); + + for (auto result : rotationPolicy.LifetimeActions) + { + bool found = false; + + for (auto original : policy.LifetimeActions) + { + if (result.Action == original.Action) + { + found = true; + break; + } + } + EXPECT_TRUE(found); + } +} + +TEST_F(KeyVaultKeyClient, DISABLED_GetRandomBytes) +{ // NEED TO DISABLE TEST FOR THE MOMENT. + // DUE TO ISSUE WITH CREATE EC HSM TEST WHICH FAILS WITH ACTUAL HSM BEING SET IN THE ENVIRONMENT + // VARIABLE FILED BUG 3563 TO FIX IT + auto const keyName = GetTestName(); + CreateHsmClient(); + auto const& client = GetClientForTest(keyName); + GetRandomBytesOptions options; + options.Count = 4; + auto result = client.GetRandomBytes(options); + EXPECT_EQ(result.Value.RandomBytes.size(), size_t(options.Count)); +} + +TEST(GetRandomBytesOptions, Serialize) +{ + GetRandomBytesOptions options; + { + options.Count = 0; + std::string result = GetRandomBytesSerializer::GetRandomBytesOptionsSerialize(options); + EXPECT_EQ(result, "{\"count\":0}"); + } + + { + options.Count = 5; + std::string result = GetRandomBytesSerializer::GetRandomBytesOptionsSerialize(options); + EXPECT_EQ(result, "{\"count\":5}"); + } + + { + options.Count = -1; + std::string result = GetRandomBytesSerializer::GetRandomBytesOptionsSerialize(options); + EXPECT_EQ(result, "{\"count\":-1}"); + } +} + +TEST(GetRandomBytesOptions, Deserialize) +{ + std::string inputString = "1234"; + auto bytes = Azure::Core::_internal::Base64Url::Base64UrlEncode( + std::vector(inputString.begin(), inputString.end())); + std::string responseText = "{\"value\": \"" + std::string(bytes.begin(), bytes.end()) + "\" }"; + + Azure::Core::Http::RawResponse rawResponse(1, 1, Azure::Core::Http::HttpStatusCode::Ok, "OK"); + rawResponse.SetBody(std::vector(responseText.begin(), responseText.end())); + + auto deserialized = GetRandomBytesSerializer::GetRandomBytesResponseDeserialize(rawResponse); + EXPECT_EQ(deserialized.size(), size_t(4)); + EXPECT_EQ(deserialized[0], uint8_t('1')); + EXPECT_EQ(deserialized[1], uint8_t('2')); + EXPECT_EQ(deserialized[2], uint8_t('3')); + EXPECT_EQ(deserialized[3], uint8_t('4')); +} + +TEST(GetRandomBytesOptions, DeserializeEmpty) +{ + std::string inputString = ""; + auto bytes = Azure::Core::Convert::Base64Encode( + std::vector(inputString.begin(), inputString.end())); + std::string responseText = "{\"value\": \"" + std::string(bytes.begin(), bytes.end()) + "\" }"; + + Azure::Core::Http::RawResponse rawResponse(1, 1, Azure::Core::Http::HttpStatusCode::Ok, "OK"); + rawResponse.SetBody(std::vector(responseText.begin(), responseText.end())); + + auto deserialized = GetRandomBytesSerializer::GetRandomBytesResponseDeserialize(rawResponse); + EXPECT_EQ(deserialized.size(), size_t(0)); +} + +TEST(KeyRotationPolicy, SerializeDeserialize1) +{ + std::string input + = "{\"id\":\"https://redacted.vault.azure.net/keys/GetKeyRotationPolicy/" + "rotationpolicy\",\"lifetimeActions\":[{\"trigger\":{\"timeAfterCreate\":\"P18M\"}," + "\"action\":{\"type\":\"Rotate\"}},{\"trigger\":{\"timeBeforeExpiry\":\"P30D\"},\"action\":" + "{\"type\":\"Notify\"}}],\"attributes\":{\"expiryTime\":\"P48M\",\"created\":1649797765," + "\"updated\":1649797765}}"; + + auto policy = KeyRotationPolicySerializer::KeyRotationPolicyDeserialize( + std::vector(input.begin(), input.end())); + + EXPECT_EQ(policy.Id, "https://redacted.vault.azure.net/keys/GetKeyRotationPolicy/rotationpolicy"); + EXPECT_EQ(policy.Attributes.ExpiryTime.Value(), "P48M"); + EXPECT_TRUE(policy.Attributes.Created); + EXPECT_TRUE(policy.Attributes.Updated); + EXPECT_EQ(policy.LifetimeActions.size(), size_t(2)); + + auto action0 = policy.LifetimeActions[0]; + EXPECT_EQ(action0.Action, LifetimeActionType::Rotate); + EXPECT_EQ(action0.Trigger.TimeAfterCreate.Value(), "P18M"); + EXPECT_FALSE(action0.Trigger.TimeBeforeExpiry); + + auto action1 = policy.LifetimeActions[1]; + EXPECT_EQ(action1.Action, LifetimeActionType::Notify); + EXPECT_EQ(action1.Trigger.TimeBeforeExpiry.Value(), "P30D"); + EXPECT_FALSE(action1.Trigger.TimeAfterCreate); + + auto serialized = KeyRotationPolicySerializer::KeyRotationPolicySerialize(policy); + + std::string serializedString + = "{\"attributes\":{\"expiryTime\":\"P48M\"},\"lifetimeActions\":[{\"action\":{\"type\":" + "\"rotate\"},\"trigger\":{\"timeAfterCreate\":\"P18M\"}},{\"action\":{\"type\":\"notify\"}," + "\"trigger\":{\"timeBeforeExpiry\":\"P30D\"}}]}"; + + EXPECT_EQ(serialized, serializedString); +} diff --git a/sdk/keyvault/azure-security-keyvault-keys/test/ut/recordings/KeyVaultKeyClient.GetKeyRotationPolicy.json b/sdk/keyvault/azure-security-keyvault-keys/test/ut/recordings/KeyVaultKeyClient.GetKeyRotationPolicy.json new file mode 100644 index 0000000000..b03fb8992c --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-keys/test/ut/recordings/KeyVaultKeyClient.GetKeyRotationPolicy.json @@ -0,0 +1,85 @@ +{ + "networkCallRecords": [ + { + "Headers": { + "content-type": "application/json", + "user-agent": "azsdk-cpp-keyvault-keys/4.3.0-beta.1 (Windows 10 Enterprise 6.3 22000 22000.1.amd64fre.co_release.210604-1628)", + "x-ms-client-request-id": "d887f7a8-65a9-4768-40a4-0c3a78a9e7a0" + }, + "Method": "POST", + "Response": { + "BODY": "{\"key\":{\"kid\":\"https://REDACTED.vault.azure.net/keys/GetKeyRotationPolicy/a8d2d57a9d1b4f2cb1ae0cefc7de5b85\",\"kty\":\"EC\",\"key_ops\":[\"sign\",\"verify\"],\"crv\":\"P-256\",\"x\":\"ZcFpPeslcbXy4DUol7h5yB3N1mHp4ZNGNy33pArvlUM\",\"y\":\"H7sZUKAQ-lOLxZQ1NhKH62CUALyhDDXp8DuDFNYIvuY\"},\"attributes\":{\"enabled\":true,\"exp\":1776202652,\"created\":1649972252,\"updated\":1649972252,\"recoveryLevel\":\"Recoverable+Purgeable\",\"recoverableDays\":90}}", + "REASON_PHRASE": "OK", + "STATUS_CODE": "200", + "cache-control": "no-cache", + "content-length": "414", + "content-type": "application/json; charset=utf-8", + "date": "Thu, 14 Apr 2022 21:37:32 GMT", + "expires": "-1", + "pragma": "no-cache", + "strict-transport-security": "max-age=31536000;includeSubDomains", + "x-content-type-options": "nosniff", + "x-ms-client-request-id": "d887f7a8-65a9-4768-40a4-0c3a78a9e7a0", + "x-ms-keyvault-network-info": "conn_type=Ipv4;addr=24.22.157.72;act_addr_fam=InterNetwork;", + "x-ms-keyvault-region": "westus3", + "x-ms-keyvault-service-version": "1.9.358.1", + "x-ms-request-id": "09612457-a092-49d2-b8a2-9d02cda2ffd5" + }, + "Url": "https://REDACTED.vault.azure.net/keys/GetKeyRotationPolicy/create?api-version=7.3" + }, + { + "Headers": { + "content-type": "application/json", + "user-agent": "azsdk-cpp-keyvault-keys/4.3.0-beta.1 (Windows 10 Enterprise 6.3 22000 22000.1.amd64fre.co_release.210604-1628)", + "x-ms-client-request-id": "95a445c7-f799-448c-57cf-68cd1ecffa81" + }, + "Method": "PUT", + "Response": { + "BODY": "{\"id\":\"https://REDACTED.vault.azure.net/keys/GetKeyRotationPolicy/rotationpolicy\",\"lifetimeActions\":[{\"trigger\":{\"timeAfterCreate\":\"P18M\"},\"action\":{\"type\":\"Rotate\"}},{\"trigger\":{\"timeBeforeExpiry\":\"P30D\"},\"action\":{\"type\":\"Notify\"}}],\"attributes\":{\"expiryTime\":\"P48M\",\"created\":1649797765,\"updated\":1649797765}}", + "REASON_PHRASE": "OK", + "STATUS_CODE": "200", + "cache-control": "no-cache", + "content-length": "314", + "content-type": "application/json; charset=utf-8", + "date": "Thu, 14 Apr 2022 21:37:33 GMT", + "expires": "-1", + "pragma": "no-cache", + "strict-transport-security": "max-age=31536000;includeSubDomains", + "x-content-type-options": "nosniff", + "x-ms-client-request-id": "95a445c7-f799-448c-57cf-68cd1ecffa81", + "x-ms-keyvault-network-info": "conn_type=Ipv4;addr=24.22.157.72;act_addr_fam=InterNetwork;", + "x-ms-keyvault-region": "westus3", + "x-ms-keyvault-service-version": "1.9.358.1", + "x-ms-request-id": "1a30a112-cb80-4fbb-a41d-7760786ba0ae" + }, + "Url": "https://REDACTED.vault.azure.net/keys/GetKeyRotationPolicy/rotationpolicy?api-version=7.3" + }, + { + "Headers": { + "content-type": "application/json", + "user-agent": "azsdk-cpp-keyvault-keys/4.3.0-beta.1 (Windows 10 Enterprise 6.3 22000 22000.1.amd64fre.co_release.210604-1628)", + "x-ms-client-request-id": "117a5643-f64a-42cf-6f0b-89ff18fb2a95" + }, + "Method": "GET", + "Response": { + "BODY": "{\"id\":\"https://REDACTED.vault.azure.net/keys/GetKeyRotationPolicy/rotationpolicy\",\"lifetimeActions\":[{\"trigger\":{\"timeAfterCreate\":\"P18M\"},\"action\":{\"type\":\"Rotate\"}},{\"trigger\":{\"timeBeforeExpiry\":\"P30D\"},\"action\":{\"type\":\"Notify\"}}],\"attributes\":{\"expiryTime\":\"P48M\",\"created\":1649797765,\"updated\":1649797765}}", + "REASON_PHRASE": "OK", + "STATUS_CODE": "200", + "cache-control": "no-cache", + "content-length": "314", + "content-type": "application/json; charset=utf-8", + "date": "Thu, 14 Apr 2022 21:37:33 GMT", + "expires": "-1", + "pragma": "no-cache", + "strict-transport-security": "max-age=31536000;includeSubDomains", + "x-content-type-options": "nosniff", + "x-ms-client-request-id": "117a5643-f64a-42cf-6f0b-89ff18fb2a95", + "x-ms-keyvault-network-info": "conn_type=Ipv4;addr=24.22.157.72;act_addr_fam=InterNetwork;", + "x-ms-keyvault-region": "westus3", + "x-ms-keyvault-service-version": "1.9.358.1", + "x-ms-request-id": "1cd18be3-f7da-430e-bfd2-4634bb37926a" + }, + "Url": "https://REDACTED.vault.azure.net/keys/GetKeyRotationPolicy/rotationpolicy?api-version=7.3" + } + ] +} diff --git a/sdk/keyvault/azure-security-keyvault-keys/test/ut/recordings/KeyVaultKeyClient.GetRandomBytes.json b/sdk/keyvault/azure-security-keyvault-keys/test/ut/recordings/KeyVaultKeyClient.GetRandomBytes.json new file mode 100644 index 0000000000..2537ff3de9 --- /dev/null +++ b/sdk/keyvault/azure-security-keyvault-keys/test/ut/recordings/KeyVaultKeyClient.GetRandomBytes.json @@ -0,0 +1,29 @@ +{ + "networkCallRecords": [ + { + "Headers": { + "content-type": "application/json", + "user-agent": "azsdk-cpp-keyvault-keys/4.3.0-beta.1 (Windows 10 Enterprise 6.3 22000 22000.1.amd64fre.co_release.210604-1628)", + "x-ms-client-request-id": "62efb9c3-6fe1-4ac9-58b7-6ceb5877ef46" + }, + "Method": "POST", + "Response": { + "BODY": "{\"value\":\"QkaFtQ\"}", + "REASON_PHRASE": "OK", + "STATUS_CODE": "200", + "cache-control": "no-cache", + "content-length": "18", + "content-security-policy": "default-src 'self'", + "content-type": "application/json; charset=utf-8", + "strict-transport-security": "max-age=31536000; includeSubDomains", + "x-content-type-options": "nosniff", + "x-frame-options": "SAMEORIGIN", + "x-ms-keyvault-network-info": "conn_type=Ipv4;addr=24.22.157.72;act_addr_fam=Ipv4;", + "x-ms-keyvault-region": "westus3", + "x-ms-request-id": "e0ed8ab4-bcec-11ec-b7b1-6045bd86d68a", + "x-ms-server-latency": "559" + }, + "Url": "https://REDACTED.managedhsm.azure.net//rng?api-version=7.3" + } + ] +}