Skip to content

Commit

Permalink
Update sorting algorithm used in storage shared-key signing (Azure#5580
Browse files Browse the repository at this point in the history
…)"

This reverts commit 6390582.
  • Loading branch information
Jinming-Hu committed May 9, 2024
1 parent fc126c4 commit ab50177
Show file tree
Hide file tree
Showing 3 changed files with 224 additions and 1 deletion.
71 changes: 71 additions & 0 deletions sdk/storage/azure-storage-blobs/test/ut/block_blob_client_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2084,4 +2084,75 @@ namespace Azure { namespace Storage { namespace Test {
EXPECT_FALSE(accountInfo.AccountKind.ToString().empty());
EXPECT_FALSE(accountInfo.IsHierarchicalNamespaceEnabled);
}

TEST_F(BlockBlobClientTest, SharedKeySigningHeaderWithSymbols)
{
class AdditionalHeaderPolicy final : public Azure::Core::Http::Policies::HttpPolicy {
public:
~AdditionalHeaderPolicy() override {}

std::unique_ptr<HttpPolicy> Clone() const override
{
return std::make_unique<AdditionalHeaderPolicy>(*this);
}

std::unique_ptr<Azure::Core::Http::RawResponse> Send(
Azure::Core::Http::Request& request,
Azure::Core::Http::Policies::NextHttpPolicy nextPolicy,
Azure::Core::Context const& context) const override
{
// cSpell:disable
request.SetHeader("x-ms-test", "val");
request.SetHeader("x-ms-test-", "val");
request.SetHeader("x-ms-test-a", "val");
request.SetHeader("x-ms-test-g", "val");
request.SetHeader("x-ms-test-Z", "val");
request.SetHeader("x-ms-testa", "val");
request.SetHeader("x-ms-testd", "val");
request.SetHeader("x-ms-testx", "val");
request.SetHeader("x-ms-test--", "val");
request.SetHeader("x-ms-test-_", "val");
request.SetHeader("x-ms-test_-", "val");
request.SetHeader("x-ms-test__", "val");
request.SetHeader("x-ms-test-a", "val");
request.SetHeader("x-ms-test-A", "val");
request.SetHeader("x-ms-test-_A", "val");
request.SetHeader("x-ms-test_a", "val");
request.SetHeader("x-ms-test_Z", "val");
request.SetHeader("x-ms-test_a_", "val");
request.SetHeader("x-ms-test_a-", "val");
request.SetHeader("x-ms-test_a-_", "val");
request.SetHeader("x-ms-testa--", "val");
request.SetHeader("x-ms-test-a-", "val");
request.SetHeader("x-ms-test--a", "val");
request.SetHeader("x-ms-testaa-", "val");
request.SetHeader("x-ms-testa-a", "val");
request.SetHeader("x-ms-test-aa", "val");

request.SetHeader("x-ms-test-!", "val");
request.SetHeader("x-ms-test-#", "val");
request.SetHeader("x-ms-test-$", "val");
request.SetHeader("x-ms-test-%", "val");
request.SetHeader("x-ms-test-&", "val");
request.SetHeader("x-ms-test-*", "val");
request.SetHeader("x-ms-test-+", "val");
request.SetHeader("x-ms-test-.", "val");
request.SetHeader("x-ms-test-^", "val");
request.SetHeader("x-ms-test-_", "val");
request.SetHeader("x-ms-test-`", "val");
request.SetHeader("x-ms-test-|", "val");
request.SetHeader("x-ms-test-~", "val");
// cSpell:enable
return nextPolicy.Send(request, context);
}
};

auto clientOptions = InitStorageClientOptions<Blobs::BlobClientOptions>();
clientOptions.PerOperationPolicies.push_back(std::make_unique<AdditionalHeaderPolicy>());
auto keyCredential
= _internal::ParseConnectionString(StandardStorageConnectionString()).KeyCredential;
auto blockBlobClient
= Blobs::BlockBlobClient(m_blockBlobClient->GetUrl(), keyCredential, clientOptions);
EXPECT_NO_THROW(blockBlobClient.GetProperties());
}
}}} // namespace Azure::Storage::Test
70 changes: 70 additions & 0 deletions sdk/storage/azure-storage-blobs/test/ut/page_blob_client_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -714,5 +714,75 @@ namespace Azure { namespace Storage { namespace Test {
}
}
}
TEST_F(PageBlobClientTest, SharedKeySigningHeaderWithSymbols)
{
class AdditionalHeaderPolicy final : public Azure::Core::Http::Policies::HttpPolicy {
public:
~AdditionalHeaderPolicy() override {}

std::unique_ptr<HttpPolicy> Clone() const override
{
return std::make_unique<AdditionalHeaderPolicy>(*this);
}

std::unique_ptr<Azure::Core::Http::RawResponse> Send(
Azure::Core::Http::Request& request,
Azure::Core::Http::Policies::NextHttpPolicy nextPolicy,
Azure::Core::Context const& context) const override
{
// cSpell:disable
request.SetHeader("x-ms-test", "val");
request.SetHeader("x-ms-test-", "val");
request.SetHeader("x-ms-test-a", "val");
request.SetHeader("x-ms-test-g", "val");
request.SetHeader("x-ms-test-Z", "val");
request.SetHeader("x-ms-testa", "val");
request.SetHeader("x-ms-testd", "val");
request.SetHeader("x-ms-testx", "val");
request.SetHeader("x-ms-test--", "val");
request.SetHeader("x-ms-test-_", "val");
request.SetHeader("x-ms-test_-", "val");
request.SetHeader("x-ms-test__", "val");
request.SetHeader("x-ms-test-a", "val");
request.SetHeader("x-ms-test-A", "val");
request.SetHeader("x-ms-test-_A", "val");
request.SetHeader("x-ms-test_a", "val");
request.SetHeader("x-ms-test_Z", "val");
request.SetHeader("x-ms-test_a_", "val");
request.SetHeader("x-ms-test_a-", "val");
request.SetHeader("x-ms-test_a-_", "val");
request.SetHeader("x-ms-testa--", "val");
request.SetHeader("x-ms-test-a-", "val");
request.SetHeader("x-ms-test--a", "val");
request.SetHeader("x-ms-testaa-", "val");
request.SetHeader("x-ms-testa-a", "val");
request.SetHeader("x-ms-test-aa", "val");

request.SetHeader("x-ms-test-!", "val");
request.SetHeader("x-ms-test-#", "val");
request.SetHeader("x-ms-test-$", "val");
request.SetHeader("x-ms-test-%", "val");
request.SetHeader("x-ms-test-&", "val");
request.SetHeader("x-ms-test-*", "val");
request.SetHeader("x-ms-test-+", "val");
request.SetHeader("x-ms-test-.", "val");
request.SetHeader("x-ms-test-^", "val");
request.SetHeader("x-ms-test-_", "val");
request.SetHeader("x-ms-test-`", "val");
request.SetHeader("x-ms-test-|", "val");
request.SetHeader("x-ms-test-~", "val");
// cSpell:enable
return nextPolicy.Send(request, context);
}
};

auto clientOptions = InitStorageClientOptions<Blobs::BlobClientOptions>();
clientOptions.PerOperationPolicies.push_back(std::make_unique<AdditionalHeaderPolicy>());
auto keyCredential
= _internal::ParseConnectionString(StandardStorageConnectionString()).KeyCredential;
auto blockBlobClient
= Blobs::BlockBlobClient(m_pageBlobClient->GetUrl(), keyCredential, clientOptions);
EXPECT_NO_THROW(blockBlobClient.GetProperties());
}

}}} // namespace Azure::Storage::Test
84 changes: 83 additions & 1 deletion sdk/storage/azure-storage-common/src/shared_key_policy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,86 @@

#include <algorithm>

namespace {
/*
* We need to imitate .Net culture-aware sorting, which is used in storage service.
* Below tables contain sort-keys for en-US culture.
*/
const static int table_lv0[128] = {
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x71c, 0x0, 0x71f, 0x721, 0x723, 0x725,
0x0, 0x0, 0x0, 0x72d, 0x803, 0x0, 0x0, 0x733, 0x0, 0xd03, 0xd1a, 0xd1c, 0xd1e,
0xd20, 0xd22, 0xd24, 0xd26, 0xd28, 0xd2a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0xe02, 0xe09, 0xe0a, 0xe1a, 0xe21, 0xe23, 0xe25, 0xe2c, 0xe32, 0xe35, 0xe36, 0xe48, 0xe51,
0xe70, 0xe7c, 0xe7e, 0xe89, 0xe8a, 0xe91, 0xe99, 0xe9f, 0xea2, 0xea4, 0xea6, 0xea7, 0xea9,
0x0, 0x0, 0x0, 0x743, 0x744, 0x748, 0xe02, 0xe09, 0xe0a, 0xe1a, 0xe21, 0xe23, 0xe25,
0xe2c, 0xe32, 0xe35, 0xe36, 0xe48, 0xe51, 0xe70, 0xe7c, 0xe7e, 0xe89, 0xe8a, 0xe91, 0xe99,
0xe9f, 0xea2, 0xea4, 0xea6, 0xea7, 0xea9, 0x0, 0x74c, 0x0, 0x750, 0x0,
};
const static int table_lv2[128] = {
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12,
0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
};
const static int table_lv4[128] = {
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8012, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8212, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
};

bool comparator(const std::string& lhs, const std::string& rhs)
{
const static std::array<const int*, 3> tables{table_lv0, table_lv2, table_lv4};
size_t curr_level = 0;
size_t i = 0;
size_t j = 0;
while (curr_level < tables.size())
{
if (curr_level == tables.size() - 1 && i != j)
{
return i > j;
}
const int weight1 = i < lhs.size() ? tables[curr_level][static_cast<uint8_t>(lhs[i])] : 0x1;
const int weight2 = j < rhs.size() ? tables[curr_level][static_cast<uint8_t>(rhs[j])] : 0x1;
if (weight1 == 0x1 && weight2 == 0x1)
{
i = 0;
j = 0;
++curr_level;
}
else if (weight1 == weight2)
{
++i;
++j;
}
else if (weight1 == 0)
{
++i;
}
else if (weight2 == 0)
{
++j;
}
else
{
return weight1 < weight2;
}
}
return false;
}
} // namespace

namespace Azure { namespace Storage { namespace _internal {

std::string SharedKeyPolicy::GetSignature(const Core::Http::Request& request) const
Expand Down Expand Up @@ -56,7 +136,9 @@ namespace Azure { namespace Storage { namespace _internal {
std::string key = Azure::Core::_internal::StringExtensions::ToLower(ite->first);
ordered_kv.emplace_back(std::make_pair(std::move(key), ite->second));
}
std::sort(ordered_kv.begin(), ordered_kv.end());
std::sort(ordered_kv.begin(), ordered_kv.end(), [](const auto& lhs, const auto& rhs) {
return comparator(lhs.first, rhs.first);
});
for (const auto& p : ordered_kv)
{
string_to_sign += p.first + ":" + p.second + "\n";
Expand Down

0 comments on commit ab50177

Please sign in to comment.