[Storage Blobs Service] Transactional MD5 and CRC64 (#456)
* MD5 * Crc64 * crc64 concatenate
This commit is contained in:
parent
0b2b688e72
commit
86a22901e9
@ -3,13 +3,65 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace Azure { namespace Storage {
|
||||
|
||||
std::string Sha256(const std::string& text);
|
||||
std::string Hmac_Sha256(const std::string& text, const std::string& key);
|
||||
std::string Base64Encode(const std::string& text);
|
||||
std::string Base64Decode(const std::string& text);
|
||||
|
||||
class Md5 {
|
||||
public:
|
||||
Md5();
|
||||
~Md5();
|
||||
|
||||
void Update(const uint8_t* data, std::size_t length);
|
||||
|
||||
std::string Digest() const;
|
||||
|
||||
static std::string Hash(const uint8_t* data, std::size_t length)
|
||||
{
|
||||
Md5 instance;
|
||||
instance.Update(data, length);
|
||||
return instance.Digest();
|
||||
}
|
||||
|
||||
static std::string Hash(const std::string& data)
|
||||
{
|
||||
return Hash(reinterpret_cast<const uint8_t*>(data.data()), data.length());
|
||||
}
|
||||
|
||||
private:
|
||||
void* m_context;
|
||||
};
|
||||
|
||||
class Crc64 {
|
||||
public:
|
||||
void Update(const uint8_t* data, std::size_t length);
|
||||
void Concatenate(const Crc64& other);
|
||||
|
||||
std::string Digest() const;
|
||||
|
||||
static std::string Hash(const uint8_t* data, std::size_t length)
|
||||
{
|
||||
Crc64 instance;
|
||||
instance.Update(data, length);
|
||||
return instance.Digest();
|
||||
}
|
||||
|
||||
static std::string Hash(const std::string& data)
|
||||
{
|
||||
return Hash(reinterpret_cast<const uint8_t*>(data.data()), data.length());
|
||||
}
|
||||
|
||||
private:
|
||||
uint64_t m_context = 0ULL;
|
||||
uint64_t m_length = 0ULL;
|
||||
};
|
||||
|
||||
namespace Details {
|
||||
std::string Sha256(const std::string& text);
|
||||
std::string HmacSha256(const std::string& text, const std::string& key);
|
||||
} // namespace Details
|
||||
}} // namespace Azure::Storage
|
||||
|
||||
@ -135,7 +135,7 @@ namespace Azure { namespace Storage { namespace Blobs {
|
||||
+ ContentEncoding + "\n" + ContentLanguage + "\n" + ContentType;
|
||||
|
||||
std::string signature
|
||||
= Base64Encode(Hmac_Sha256(stringToSign, Base64Decode(credential.GetAccountKey())));
|
||||
= Base64Encode(Details::HmacSha256(stringToSign, Base64Decode(credential.GetAccountKey())));
|
||||
|
||||
UriBuilder builder;
|
||||
builder.AppendQuery("sv", Version);
|
||||
@ -219,7 +219,7 @@ namespace Azure { namespace Storage { namespace Blobs {
|
||||
+ "\n" + ContentType;
|
||||
|
||||
std::string signature
|
||||
= Base64Encode(Hmac_Sha256(stringToSign, Base64Decode(userDelegationKey.Value)));
|
||||
= Base64Encode(Details::HmacSha256(stringToSign, Base64Decode(userDelegationKey.Value)));
|
||||
|
||||
UriBuilder builder;
|
||||
builder.AppendQuery("sv", Version);
|
||||
|
||||
@ -102,7 +102,7 @@ namespace Azure { namespace Storage {
|
||||
+ "\n";
|
||||
|
||||
std::string signature
|
||||
= Base64Encode(Hmac_Sha256(stringToSign, Base64Decode(credential.GetAccountKey())));
|
||||
= Base64Encode(Details::HmacSha256(stringToSign, Base64Decode(credential.GetAccountKey())));
|
||||
|
||||
UriBuilder builder;
|
||||
builder.AppendQuery("sv", Version);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -84,6 +84,7 @@ namespace Azure { namespace Storage {
|
||||
// remove last linebreak
|
||||
string_to_sign.pop_back();
|
||||
|
||||
return Base64Encode(Hmac_Sha256(string_to_sign, Base64Decode(m_credential->GetAccountKey())));
|
||||
return Base64Encode(
|
||||
Details::HmacSha256(string_to_sign, Base64Decode(m_credential->GetAccountKey())));
|
||||
}
|
||||
}} // namespace Azure::Storage
|
||||
|
||||
@ -452,7 +452,7 @@ namespace Azure { namespace Storage { namespace Test {
|
||||
aes256Key.resize(32);
|
||||
RandomBuffer(&aes256Key[0], aes256Key.size());
|
||||
key.Key = Base64Encode(aes256Key);
|
||||
key.KeyHash = Base64Encode(Sha256(aes256Key));
|
||||
key.KeyHash = Base64Encode(Details::Sha256(aes256Key));
|
||||
key.Algorithm = Blobs::EncryptionAlgorithmType::Aes256;
|
||||
return key;
|
||||
};
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include "page_blob_client_test.hpp"
|
||||
#include "common/crypt.hpp"
|
||||
|
||||
namespace Azure { namespace Storage { namespace Test {
|
||||
|
||||
@ -203,4 +204,44 @@ namespace Azure { namespace Storage { namespace Test {
|
||||
m_pageBlobClient->BreakLease(options);
|
||||
}
|
||||
|
||||
TEST_F(PageBlobClientTest, ContentMd5)
|
||||
{
|
||||
std::vector<uint8_t> blobContent;
|
||||
blobContent.resize(static_cast<std::size_t>(4_KB));
|
||||
RandomBuffer(reinterpret_cast<char*>(&blobContent[0]), blobContent.size());
|
||||
|
||||
auto pageBlobClient = Azure::Storage::Blobs::PageBlobClient::CreateFromConnectionString(
|
||||
StandardStorageConnectionString(), m_containerName, RandomString());
|
||||
pageBlobClient.Create(blobContent.size(), m_blobUploadOptions);
|
||||
auto pageContent = Azure::Core::Http::MemoryBodyStream(blobContent.data(), blobContent.size());
|
||||
|
||||
Blobs::UploadPageBlobPagesOptions options;
|
||||
options.ContentMd5 = Base64Encode(Md5::Hash(blobContent.data(), blobContent.size()));
|
||||
EXPECT_NO_THROW(pageBlobClient.UploadPages(&pageContent, 0, options));
|
||||
|
||||
pageContent.Rewind();
|
||||
options.ContentMd5 = c_dummyMd5;
|
||||
EXPECT_THROW(pageBlobClient.UploadPages(&pageContent, 0, options), StorageError);
|
||||
}
|
||||
|
||||
TEST_F(PageBlobClientTest, ContentCrc64)
|
||||
{
|
||||
std::vector<uint8_t> blobContent;
|
||||
blobContent.resize(static_cast<std::size_t>(4_KB));
|
||||
RandomBuffer(reinterpret_cast<char*>(&blobContent[0]), blobContent.size());
|
||||
|
||||
auto pageBlobClient = Azure::Storage::Blobs::PageBlobClient::CreateFromConnectionString(
|
||||
StandardStorageConnectionString(), m_containerName, RandomString());
|
||||
pageBlobClient.Create(blobContent.size(), m_blobUploadOptions);
|
||||
auto pageContent = Azure::Core::Http::MemoryBodyStream(blobContent.data(), blobContent.size());
|
||||
|
||||
Blobs::UploadPageBlobPagesOptions options;
|
||||
options.ContentCrc64 = Base64Encode(Crc64::Hash(blobContent.data(), blobContent.size()));
|
||||
EXPECT_NO_THROW(pageBlobClient.UploadPages(&pageContent, 0, options));
|
||||
|
||||
pageContent.Rewind();
|
||||
options.ContentCrc64 = c_dummyCrc64;
|
||||
EXPECT_THROW(pageBlobClient.UploadPages(&pageContent, 0, options), StorageError);
|
||||
}
|
||||
|
||||
}}} // namespace Azure::Storage::Test
|
||||
|
||||
@ -19,17 +19,109 @@ namespace Azure { namespace Storage { namespace Test {
|
||||
|
||||
TEST(CryptFunctionsTest, Sha256)
|
||||
{
|
||||
EXPECT_EQ(Base64Encode(Sha256("")), "47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=");
|
||||
EXPECT_EQ(Base64Encode(Sha256("Hello Azure!")), "Mjzwx2mqGHb9FSgjm33ShNmXYndkgvwA6tQmEiskOHg=");
|
||||
EXPECT_EQ(Base64Encode(Details::Sha256("")), "47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=");
|
||||
EXPECT_EQ(
|
||||
Base64Encode(Details::Sha256("Hello Azure!")),
|
||||
"Mjzwx2mqGHb9FSgjm33ShNmXYndkgvwA6tQmEiskOHg=");
|
||||
}
|
||||
|
||||
TEST(CryptFunctionsTest, HmacSha256)
|
||||
{
|
||||
std::string key = "8CwtGFF1mGR4bPEP9eZ0x1fxKiQ3Ca5N";
|
||||
EXPECT_EQ(Base64Encode(Hmac_Sha256("", key)), "fFy2T+EuCvAgouw/vB/RAJ75z7jwTj+uiURebkFKF5M=");
|
||||
EXPECT_EQ(
|
||||
Base64Encode(Hmac_Sha256("Hello Azure!", key)),
|
||||
Base64Encode(Details::HmacSha256("", key)), "fFy2T+EuCvAgouw/vB/RAJ75z7jwTj+uiURebkFKF5M=");
|
||||
EXPECT_EQ(
|
||||
Base64Encode(Details::HmacSha256("Hello Azure!", key)),
|
||||
"+SBESxQVhI53mSEdZJcCBpdBkaqwzfPaVYZMAf5LP3c=");
|
||||
}
|
||||
|
||||
TEST(CryptFunctionsTest, Md5)
|
||||
{
|
||||
EXPECT_EQ(Base64Encode(Md5::Hash("")), "1B2M2Y8AsgTpgAmY7PhCfg==");
|
||||
EXPECT_EQ(Base64Encode(Md5::Hash("Hello Azure!")), "Pz8543xut4RVSbb2g52Mww==");
|
||||
|
||||
auto data = RandomBuffer(static_cast<std::size_t>(16_MB));
|
||||
Md5 md5Instance;
|
||||
|
||||
std::size_t length = 0;
|
||||
while (length < data.size())
|
||||
{
|
||||
std::size_t s = static_cast<std::size_t>(RandomInt(0, 4_MB));
|
||||
s = std::min(s, data.size() - length);
|
||||
md5Instance.Update(&data[length], s);
|
||||
md5Instance.Update(&data[length], 0);
|
||||
length += s;
|
||||
}
|
||||
EXPECT_EQ(md5Instance.Digest(), Md5::Hash(data.data(), data.size()));
|
||||
}
|
||||
|
||||
TEST(CryptFunctionsTest, Crc64)
|
||||
{
|
||||
EXPECT_EQ(Base64Encode(Crc64::Hash("")), "AAAAAAAAAAA=");
|
||||
EXPECT_EQ(Base64Encode(Crc64::Hash("Hello Azure!")), "DtjZpL9/o8c=");
|
||||
|
||||
auto data = RandomBuffer(static_cast<std::size_t>(16_MB));
|
||||
Crc64 crc64Instance;
|
||||
|
||||
std::size_t length = 0;
|
||||
while (length < data.size())
|
||||
{
|
||||
std::size_t s = static_cast<std::size_t>(RandomInt(0, 4_MB));
|
||||
s = std::min(s, data.size() - length);
|
||||
crc64Instance.Update(&data[length], s);
|
||||
crc64Instance.Update(&data[length], 0);
|
||||
length += s;
|
||||
}
|
||||
EXPECT_EQ(crc64Instance.Digest(), Crc64::Hash(data.data(), data.size()));
|
||||
|
||||
// Test concatenate
|
||||
crc64Instance = Crc64();
|
||||
std::string allData;
|
||||
while (allData.length() < 16_MB)
|
||||
{
|
||||
{
|
||||
Crc64 instance2;
|
||||
for (auto i = RandomInt(0, 5); i > 0; --i)
|
||||
{
|
||||
std::size_t s = static_cast<std::size_t>(RandomInt(0, 512_KB));
|
||||
std::string data2;
|
||||
data2.resize(s);
|
||||
RandomBuffer(&data2[0], s);
|
||||
instance2.Update(reinterpret_cast<const uint8_t*>(data2.data()), data2.length());
|
||||
allData += data2;
|
||||
}
|
||||
crc64Instance.Concatenate(instance2);
|
||||
}
|
||||
|
||||
switch (RandomInt(0, 2))
|
||||
{
|
||||
case 0: {
|
||||
std::string data2;
|
||||
crc64Instance.Update(reinterpret_cast<const uint8_t*>(data2.data()), data2.length());
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
Crc64 instance2;
|
||||
crc64Instance.Concatenate(instance2);
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
std::size_t s = static_cast<std::size_t>(RandomInt(0, 512_KB));
|
||||
std::string data2;
|
||||
data2.resize(s);
|
||||
RandomBuffer(&data2[0], s);
|
||||
crc64Instance.Update(reinterpret_cast<const uint8_t*>(data2.data()), data2.length());
|
||||
allData += data2;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
EXPECT_EQ(
|
||||
crc64Instance.Digest(),
|
||||
Crc64::Hash(reinterpret_cast<const uint8_t*>(allData.data()), allData.size()));
|
||||
}
|
||||
|
||||
}}} // namespace Azure::Storage::Test
|
||||
|
||||
@ -127,7 +127,13 @@ namespace Azure { namespace Storage { namespace Test {
|
||||
|
||||
static thread_local std::mt19937_64 random_generator(std::random_device{}());
|
||||
|
||||
static char random_char()
|
||||
uint64_t RandomInt(uint64_t minNumber, uint64_t maxNumber)
|
||||
{
|
||||
std::uniform_int_distribution<uint64_t> distribution(minNumber, maxNumber);
|
||||
return distribution(random_generator);
|
||||
}
|
||||
|
||||
static char RandomChar()
|
||||
{
|
||||
const char charset[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
std::uniform_int_distribution<std::size_t> distribution(0, sizeof(charset) - 2);
|
||||
@ -138,7 +144,7 @@ namespace Azure { namespace Storage { namespace Test {
|
||||
{
|
||||
std::string str;
|
||||
str.resize(size);
|
||||
std::generate(str.begin(), str.end(), random_char);
|
||||
std::generate(str.begin(), str.end(), RandomChar);
|
||||
return str;
|
||||
}
|
||||
|
||||
@ -171,7 +177,7 @@ namespace Azure { namespace Storage { namespace Test {
|
||||
|
||||
while (uintptr_t(start_addr) % rand_int_size != 0 && start_addr < end_addr)
|
||||
{
|
||||
*(start_addr++) = random_char();
|
||||
*(start_addr++) = RandomChar();
|
||||
}
|
||||
|
||||
std::uniform_int_distribution<uint64_t> distribution(
|
||||
@ -183,7 +189,7 @@ namespace Azure { namespace Storage { namespace Test {
|
||||
}
|
||||
while (start_addr < end_addr)
|
||||
{
|
||||
*(start_addr++) = random_char();
|
||||
*(start_addr++) = RandomChar();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <limits>
|
||||
|
||||
namespace Azure { namespace Storage { namespace Test {
|
||||
|
||||
@ -39,6 +40,12 @@ namespace Azure { namespace Storage { namespace Test {
|
||||
}
|
||||
|
||||
constexpr static const char* c_dummyETag = "0x8D83B58BDF51D75";
|
||||
constexpr static const char* c_dummyMd5 = "tQbD1aMPeB+LiPffUwFQJQ==";
|
||||
constexpr static const char* c_dummyCrc64 = "+DNR5PON4EM=";
|
||||
|
||||
uint64_t RandomInt(
|
||||
uint64_t minNumber = std::numeric_limits<uint64_t>::min(),
|
||||
uint64_t maxNumber = std::numeric_limits<uint64_t>::max());
|
||||
|
||||
std::string RandomString(size_t size = 10);
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user