From ab501779005f561f3e8adb3f28f6fd1829106837 Mon Sep 17 00:00:00 2001 From: Jinming Hu Date: Thu, 9 May 2024 11:42:43 +0800 Subject: [PATCH] Update sorting algorithm used in storage shared-key signing (#5580)" This reverts commit 6390582951d9fb501c8592a6abb0de6f7b5c34b6. --- .../test/ut/block_blob_client_test.cpp | 71 ++++++++++++++++ .../test/ut/page_blob_client_test.cpp | 70 ++++++++++++++++ .../src/shared_key_policy.cpp | 84 ++++++++++++++++++- 3 files changed, 224 insertions(+), 1 deletion(-) diff --git a/sdk/storage/azure-storage-blobs/test/ut/block_blob_client_test.cpp b/sdk/storage/azure-storage-blobs/test/ut/block_blob_client_test.cpp index b5374a0913..b2a5c76fb9 100644 --- a/sdk/storage/azure-storage-blobs/test/ut/block_blob_client_test.cpp +++ b/sdk/storage/azure-storage-blobs/test/ut/block_blob_client_test.cpp @@ -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 Clone() const override + { + return std::make_unique(*this); + } + + std::unique_ptr 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(); + clientOptions.PerOperationPolicies.push_back(std::make_unique()); + auto keyCredential + = _internal::ParseConnectionString(StandardStorageConnectionString()).KeyCredential; + auto blockBlobClient + = Blobs::BlockBlobClient(m_blockBlobClient->GetUrl(), keyCredential, clientOptions); + EXPECT_NO_THROW(blockBlobClient.GetProperties()); + } }}} // namespace Azure::Storage::Test diff --git a/sdk/storage/azure-storage-blobs/test/ut/page_blob_client_test.cpp b/sdk/storage/azure-storage-blobs/test/ut/page_blob_client_test.cpp index 720a1886c4..2e0c2c92a3 100644 --- a/sdk/storage/azure-storage-blobs/test/ut/page_blob_client_test.cpp +++ b/sdk/storage/azure-storage-blobs/test/ut/page_blob_client_test.cpp @@ -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 Clone() const override + { + return std::make_unique(*this); + } + + std::unique_ptr 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(); + clientOptions.PerOperationPolicies.push_back(std::make_unique()); + auto keyCredential + = _internal::ParseConnectionString(StandardStorageConnectionString()).KeyCredential; + auto blockBlobClient + = Blobs::BlockBlobClient(m_pageBlobClient->GetUrl(), keyCredential, clientOptions); + EXPECT_NO_THROW(blockBlobClient.GetProperties()); + } }}} // namespace Azure::Storage::Test diff --git a/sdk/storage/azure-storage-common/src/shared_key_policy.cpp b/sdk/storage/azure-storage-common/src/shared_key_policy.cpp index 80dc602f7f..3a8d7e0b37 100644 --- a/sdk/storage/azure-storage-common/src/shared_key_policy.cpp +++ b/sdk/storage/azure-storage-common/src/shared_key_policy.cpp @@ -10,6 +10,86 @@ #include +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 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(lhs[i])] : 0x1; + const int weight2 = j < rhs.size() ? tables[curr_level][static_cast(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 @@ -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";