Skip to content

Commit

Permalink
Release key API (#3622)
Browse files Browse the repository at this point in the history
* step1

* part2

* code maybe

* working to create exportable with release policy

* still not working

* Release Key Works now

* format

* qfe

* clean build issues

* build fixes

* PR comments

* cspell

* rework the test to use the source keys instead of jwk. needed to be restored for live tests

* update location
  • Loading branch information
gearama authored May 9, 2022
1 parent 6b2a32f commit 00a53df
Show file tree
Hide file tree
Showing 68 changed files with 2,014 additions and 1,773 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,24 @@ namespace Azure { namespace Security { namespace KeyVault { namespace Keys {
std::string const& name,
Azure::Core::Context const& context = Azure::Core::Context()) const;

/**
* @brief Releases a key.
*
* @remark The release key operation is applicable to all key types. The target key must be
* marked exportable. This operation requires the keys/release permission.
*
* @param name The name of the key.
* @param version The key version.
* @param options The options for the key release operation.
* @param context A cancellation token controlling the request lifetime.
* @return ReleaseKeyResult object.
*/
Azure::Response<ReleaseKeyResult> ReleaseKey(
std::string const& name,
std::string const& version,
KeyReleaseOptions const& options,
Azure::Core::Context const& context = Azure::Core::Context()) const;

/**
* @brief Gets the public part of a deleted key.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1104,4 +1104,17 @@ namespace Azure { namespace Security { namespace KeyVault { namespace Keys {
*/
std::vector<uint8_t> RandomBytes;
};

/**
* @brief The release result, containing the released key.
*
*/
struct ReleaseKeyResult
{
/**
* @brief A signed object containing the released key.
*
*/
std::string Value;
};
}}}} // namespace Azure::Security::KeyVault::Keys
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ void Azure::Security::KeyVault::Keys::_detail::JsonWebKeySerializer::JsonWebKeyS
jwk.CurveName, destJson, _detail::CurveNamePropertyName, [](KeyCurveName const& value) {
return value.ToString();
});
if (jwk.Id.length() > 0)
{
destJson[_detail::KeyIdPropertyName] = jwk.Id;
}

// fields
WriteJsonIfVectorHasData(jwk.N, destJson, _detail::NPropertyName);
Expand Down
21 changes: 20 additions & 1 deletion sdk/keyvault/azure-security-keyvault-keys/src/key_client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
#include <azure/core/http/http.hpp>
#include <azure/core/http/policies/policy.hpp>
#include <azure/core/internal/http/pipeline.hpp>

#include <azure/keyvault/shared/keyvault_shared.hpp>

#include "azure/keyvault/keys/key_client.hpp"
Expand Down Expand Up @@ -236,6 +235,26 @@ Azure::Security::KeyVault::Keys::DeleteKeyOperation KeyClient::StartDeleteKey(
std::make_shared<KeyClient>(*this), std::move(responseT));
}

Azure::Response<ReleaseKeyResult> KeyClient::ReleaseKey(
std::string const& name,
std::string const& version,
KeyReleaseOptions const& options,
Azure::Core::Context const& context) const
{
auto payload = _detail::KeyReleaseOptionsSerializer::KeyReleaseOptionsSerialize(options);
Azure::Core::IO::MemoryBodyStream payloadStream(
reinterpret_cast<const uint8_t*>(payload.data()), payload.size());

// Request and settings
auto request = CreateRequest(
HttpMethod::Post, {_detail::KeysPath, name, version, _detail::ReleaseValue}, &payloadStream);
request.SetHeader(HttpShared::ContentType, HttpShared::ApplicationJson);
// Send and parse respone
auto rawResponse = SendRequest(request, context);
auto value = _detail::KeyReleaseOptionsSerializer::KeyReleaseOptionsDeserialize(*rawResponse);
return Azure::Response<ReleaseKeyResult>(value, std::move(rawResponse));
}

Azure::Security::KeyVault::Keys::RecoverDeletedKeyOperation KeyClient::StartRecoverDeletedKey(
std::string const& name,
Azure::Core::Context const& context) const
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,17 @@ Azure::Security::KeyVault::Keys::_detail::KeyReleaseOptionsSerializer::KeyReleas

return payload.dump();
}

ReleaseKeyResult
Azure::Security::KeyVault::Keys::_detail::KeyReleaseOptionsSerializer::KeyReleaseOptionsDeserialize(
Azure::Core::Http::RawResponse const& rawResponse)
{
ReleaseKeyResult result;

auto const& body = rawResponse.GetBody();
auto jsonParser = Azure::Core::Json::_internal::json::parse(body);

result.Value = jsonParser[_detail::ValueParameterValue].get<std::string>();

return result;
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,13 @@ Azure::Security::KeyVault::Keys::_detail::KeyReleasePolicySerializer::KeyRelease

KeyReleasePolicy
Azure::Security::KeyVault::Keys::_detail::KeyReleasePolicySerializer::KeyReleasePolicyDeserialize(
Azure::Core::Json::_internal::json const& response)
Azure::Core::Json::_internal::json const& rawResponse)
{
KeyReleasePolicy policy;
auto decodedData = Base64Url::Base64UrlDecode(response[_detail::DataValue].get<std::string>());
auto decodedData = Base64Url::Base64UrlDecode(rawResponse[_detail::DataValue].get<std::string>());

policy.ContentType = response[_detail::ContentTypeValue].get<std::string>();
policy.Immutable = response[_detail::ImmutableValue].get<bool>();
policy.ContentType = rawResponse[_detail::ContentTypeValue].get<std::string>();
policy.Immutable = rawResponse[_detail::ImmutableValue].get<bool>();
policy.Data = std::string(decodedData.begin(), decodedData.end());

return policy;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,8 @@ namespace Azure { namespace Security { namespace KeyVault { namespace Keys { nam
class KeyReleaseOptionsSerializer final {
public:
static std::string KeyReleaseOptionsSerialize(KeyReleaseOptions const& keyReleaseOptions);
static ReleaseKeyResult KeyReleaseOptionsDeserialize(
Azure::Core::Http::RawResponse const& rawResponse);
};

/**************** KeyReleasePolicySerializer ************/
Expand All @@ -145,6 +147,6 @@ namespace Azure { namespace Security { namespace KeyVault { namespace Keys { nam
static Azure::Core::Json::_internal::json KeyReleasePolicySerialize(
KeyReleasePolicy const& policy);
static KeyReleasePolicy KeyReleasePolicyDeserialize(
Azure::Core::Json::_internal::json const& response);
Azure::Core::Json::_internal::json const& rawResponse);
};
}}}}} // namespace Azure::Security::KeyVault::Keys::_detail
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ add_compile_definitions(AZURE_TEST_RECORDING_DIR="${CMAKE_CURRENT_LIST_DIR}")
################## Unit Tests ##########################
add_executable (
azure-security-keyvault-keys-test

key_client_backup_test_live.cpp
key_client_base_test.hpp
key_client_base_test.hpp
Expand All @@ -29,7 +30,8 @@ add_executable (
macro_guard.cpp
mocked_client_test.cpp
mocked_transport_adapter_test.hpp
)
test_consts.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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,11 @@
#include <gtest/gtest.h>

#include <azure/core/context.hpp>
#include <azure/core/test/test_base.hpp>
#include <azure/core/uuid.hpp>
#include <azure/identity/client_secret_credential.hpp>
#include <azure/keyvault/keys/cryptography/cryptography_client.hpp>
#include <azure/keyvault/keyvault_keys.hpp>

#include <azure/core/test/test_base.hpp>

#include <chrono>
#include <cstdio>
#include <iostream>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@

#include "../../azure-security-attestation/src/private/crypto/inc/crypto.hpp"
#include "key_client_base_test.hpp"

#include "private/key_constants.hpp"
#include "private/key_serializers.hpp"
#include "test_consts.hpp"
#include "gtest/gtest.h"
#include <azure/attestation.hpp>
#include <azure/attestation/attestation_client_options.hpp>
#include <azure/core/base64.hpp>
#include <azure/core/internal/json/json.hpp>
#include <azure/keyvault/keyvault_keys.hpp>
Expand All @@ -16,6 +19,10 @@
using namespace Azure::Core::_internal;
using namespace Azure::Security::KeyVault::Keys;
using namespace Azure::Security::KeyVault::Keys::Test;
using namespace Azure::Security::Attestation;
using namespace Azure::Core::Http;
using namespace Azure::Core::Json::_internal;
using namespace Azure::Security::KeyVault::Keys::Cryptography;

TEST_F(KeyVaultKeyClient, CreateKey)
{
Expand Down Expand Up @@ -228,6 +235,82 @@ TEST_F(KeyVaultKeyClient, CreateRsaHsmKey)
EXPECT_FALSE(keyResponse.Value.Properties.ReleasePolicy.HasValue());
}
}
std::string BinaryToHexString(std::vector<uint8_t> const& src)
{
static constexpr char hexMap[]
= {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
std::string output(static_cast<size_t>(src.size()) * 2, ' ');
const uint8_t* input = src.data();

for (size_t i = 0; i < src.size(); i++)
{
output[2 * i] = hexMap[(input[i] & 0xF0) >> 4];
output[2 * i + 1] = hexMap[input[i] & 0x0F];
}

return output;
}

TEST_F(KeyVaultKeyClient, ReleaseKey)
{
auto const keyName = GetTestName() + "2";
auto const& client = GetClientForTest(keyName);

auto restored = client.RestoreKeyBackup(Base64Url::Base64UrlDecode(RawBackupKey));

Azure::Core::Json::_internal::json keysJson;
Azure::Core::Json::_internal::json keyJson;
Azure::Security::KeyVault::Keys::_detail::JsonWebKeySerializer::JsonWebKeySerialize(
restored.Value.Key, keyJson);
keysJson["keys"].emplace_back(keyJson);
auto keySerializedJWK = keysJson.dump();

auto decodedGeneratedToken = Base64Url::Base64UrlDecode(Base64UrlEncodedGeneratedQuote);

AttestationClientOptions attestationOptions;
attestationOptions.TokenValidationOptions.ValidationTimeSlack = 10s;

Azure::Security::Attestation::AttestationClient attestationClient(
AttestationServiceUrl(), attestationOptions);
attestationClient.RetrieveResponseValidationCollateral();

auto attestResponse = attestationClient.AttestOpenEnclave(
decodedGeneratedToken,
AttestOptions{AttestationData{
std::vector<uint8_t>(keySerializedJWK.begin(), keySerializedJWK.end()),
AttestationDataType::Binary}});

Azure::Security::KeyVault::Keys::CreateKeyOptions options;
options.KeyOperations.push_back(Azure::Security::KeyVault::Keys::KeyOperation::Sign);
options.KeyOperations.push_back(Azure::Security::KeyVault::Keys::KeyOperation::Verify);
options.ReleasePolicy = KeyReleasePolicy();
options.ReleasePolicy.Value().Immutable = false;
// cspell:disable
std::string dataStr = R"({
"anyOf" : [ {
"allOf" : [{"claim" : "x-ms-sgx-mrsigner", "equals" : ")"
+ BinaryToHexString(attestResponse.Value.Body.SgxMrSigner.Value()) + R"("
}],
"authority" : ")"
+ AttestationServiceUrl() + R"("
}],
"version" : "1.0.0"
})";
// cspell:enable
auto jsonParser = json::parse(dataStr);
options.ReleasePolicy.Value().Data = jsonParser.dump();
options.Exportable = true;
auto keyResponse
= client.CreateKey(keyName, Azure::Security::KeyVault::Keys::KeyVaultKeyType::EcHsm, options);

KeyReleaseOptions relOpt;
relOpt.Target = attestResponse.Value.RawToken;
relOpt.Encryption = KeyEncryptionAlgorithm::RSA_AES_KEY_WRAP_256;
auto result2 = client.ReleaseKey(keyName, keyResponse.Value.Properties.Version, relOpt);
EXPECT_NE(result2.Value.Value.length(), size_t(0));
EXPECT_EQ(result2.RawResponse->GetStatusCode(), HttpStatusCode::Ok);
}

TEST_F(KeyVaultKeyClient, CreateKeyWithReleasePolicyOptions)
{
auto const keyName = GetTestName();
Expand All @@ -245,7 +328,7 @@ TEST_F(KeyVaultKeyClient, CreateKeyWithReleasePolicyOptions)
"} ],"
" \"version\" : \"1.0.0\""
"} ";
auto jsonParser = Azure::Core::Json::_internal::json::parse(dataStr);
auto jsonParser = json::parse(dataStr);
options.ReleasePolicy.Value().Data = jsonParser.dump();
options.Exportable = true;
{
Expand Down Expand Up @@ -285,8 +368,7 @@ TEST_F(KeyVaultKeyClient, CreateKeyWithReleasePolicyOptions)
EXPECT_FALSE(policy.Immutable);

EXPECT_EQ(
Azure::Core::Json::_internal::json::parse(options.ReleasePolicy.Value().Data)
.dump(1, ' ', true),
Azure::Core::Json::_internal::json::parse(policy.Data).dump(1, ' ', true));
json::parse(options.ReleasePolicy.Value().Data).dump(1, ' ', true),
json::parse(policy.Data).dump(1, ' ', true));
}
}
Loading

0 comments on commit 00a53df

Please sign in to comment.