Skip to content

Commit

Permalink
[SSL clean-up] Avoid memory allocations for SSL module, clean-up and …
Browse files Browse the repository at this point in the history
…factorize code
  • Loading branch information
sjanel committed Apr 1, 2024
1 parent 292878a commit 1234a5f
Show file tree
Hide file tree
Showing 15 changed files with 250 additions and 174 deletions.
34 changes: 21 additions & 13 deletions src/api/common/include/ssl_sha.hpp
Original file line number Diff line number Diff line change
@@ -1,32 +1,40 @@
#pragma once

#include <array>
#include <climits>
#include <cstddef>
#include <cstdint>
#include <span>
#include <string_view>

#include "cct_fixedcapacityvector.hpp"
#include "cct_string.hpp"

namespace cct::ssl {

/// @brief Helper type containing the number of bytes of the SHA
enum class ShaType : uint8_t { kSha256 = 256 / CHAR_BIT, kSha512 = 512 / CHAR_BIT };

std::string_view GetOpenSSLVersion();

/// @brief Helper type containing the number of bytes of the SHA
enum class ShaType : int16_t { kSha256 = 256 / CHAR_BIT, kSha512 = 512 / CHAR_BIT };
using Md256 = std::array<char, static_cast<std::size_t>(ShaType::kSha256)>;
using Md512 = std::array<char, static_cast<std::size_t>(ShaType::kSha512)>;

using Md256 = FixedCapacityVector<char, static_cast<int16_t>(ShaType::kSha256)>;
using Md512 = FixedCapacityVector<char, static_cast<int16_t>(ShaType::kSha512)>;
using Sha256HexArray = std::array<char, 2UL * static_cast<std::size_t>(ShaType::kSha256)>;
using Sha512HexArray = std::array<char, 2UL * static_cast<std::size_t>(ShaType::kSha512)>;

/// @brief Compute Sha256 from 'data'
Md256 Sha256(std::string_view data);
using Sha256DigestArray = std::array<char, 2UL * static_cast<std::size_t>(ShaType::kSha256)>;
using Sha512DigestArray = std::array<char, 2UL * static_cast<std::size_t>(ShaType::kSha512)>;

Md256 Sha256Bin(std::string_view data, std::string_view secret);
Md512 Sha512Bin(std::string_view data, std::string_view secret);

Md512 ShaBin(ShaType shaType, std::string_view data, std::string_view secret);
Md256 Sha256(std::string_view data);

string ShaHex(ShaType shaType, std::string_view data, std::string_view secret);
Sha256HexArray Sha256Hex(std::string_view data, std::string_view secret);
Sha512HexArray Sha512Hex(std::string_view data, std::string_view secret);

string ShaDigest(ShaType shaType, std::string_view data);
Sha256DigestArray Sha256Digest(std::string_view data);
Sha512DigestArray Sha512Digest(std::string_view data);

string ShaDigest(ShaType shaType, std::span<const string> data);
Sha256DigestArray Sha256Digest(std::span<const std::string_view> data);
Sha512DigestArray Sha512Digest(std::span<const std::string_view> data);

} // namespace cct::ssl
120 changes: 83 additions & 37 deletions src/api/common/src/ssl_sha.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,89 +11,135 @@
#include <string_view>

#include "cct_exception.hpp"
#include "cct_string.hpp"
#include "codec.hpp"
#include "char-hexadecimal-converter.hpp"

namespace cct::ssl {
namespace {

auto ShaDigestLen(ShaType shaType) { return static_cast<unsigned int>(shaType); }
constexpr auto ShaDigestLen(ShaType shaType) { return static_cast<unsigned int>(shaType); }

const EVP_MD* GetEVPMD(ShaType shaType) { return shaType == ShaType::kSha256 ? EVP_sha256() : EVP_sha512(); }
const EVP_MD* GetEVP_MD(ShaType shaType) { return shaType == ShaType::kSha256 ? EVP_sha256() : EVP_sha512(); }
} // namespace

Md256 Sha256(std::string_view data) {
static_assert(SHA256_DIGEST_LENGTH == static_cast<unsigned int>(ShaType::kSha256));

Md256 ret(static_cast<string::size_type>(SHA256_DIGEST_LENGTH));
Md256 ret;

SHA256(reinterpret_cast<const unsigned char*>(data.data()), data.size(),
reinterpret_cast<unsigned char*>(ret.data()));

return ret;
}

std::string_view GetOpenSSLVersion() { return OPENSSL_VERSION_TEXT; }
std::string_view GetOpenSSLVersion() {
static constexpr std::string_view kOpenSSLVersion = OPENSSL_VERSION_TEXT;
return kOpenSSLVersion;
}

namespace {

template <ShaType shaType>
auto ShaBin(std::string_view data, std::string_view secret) {
static constexpr unsigned int kExpectedLen = ShaDigestLen(shaType);

Md512 ShaBin(ShaType shaType, std::string_view data, std::string_view secret) {
unsigned int len = ShaDigestLen(shaType);
Md512 binData(static_cast<Md512::size_type>(len));
unsigned int len = kExpectedLen;
std::array<char, kExpectedLen> binData;

HMAC(GetEVPMD(shaType), secret.data(), static_cast<int>(secret.size()),
HMAC(GetEVP_MD(shaType), secret.data(), static_cast<int>(secret.size()),
reinterpret_cast<const unsigned char*>(data.data()), data.size(),
reinterpret_cast<unsigned char*>(binData.data()), &len);

if (len != binData.size()) {
throw exception("Unexpected result from HMAC: expected len {}, got {}", binData.size(), len);
if (len != kExpectedLen) {
throw exception("Unexpected result from HMAC: expected len {}, got {}", kExpectedLen, len);
}

return binData;
}

string ShaHex(ShaType shaType, std::string_view data, std::string_view secret) {
unsigned int len = ShaDigestLen(shaType);
unsigned char binData[EVP_MAX_MD_SIZE];
template <std::size_t N>
std::array<char, 2UL * N> BinToLowerHex(const std::array<char, N>& binData) {
std::array<char, 2UL * N> ret;

auto out = ret.data();

for (auto beg = binData.begin(), end = binData.end(); beg != end; ++beg) {
out = to_lower_hex(*beg, out);
}
return ret;
}

template <ShaType shaType>
auto ShaHex(std::string_view data, std::string_view secret) {
return BinToLowerHex(ShaBin<shaType>(data, secret));
}

} // namespace

Md256 Sha256Bin(std::string_view data, std::string_view secret) { return ShaBin<ShaType::kSha256>(data, secret); }

HMAC(GetEVPMD(shaType), secret.data(), static_cast<int>(secret.size()),
reinterpret_cast<const unsigned char*>(data.data()), data.size(), binData, &len);
Md512 Sha512Bin(std::string_view data, std::string_view secret) { return ShaBin<ShaType::kSha512>(data, secret); }

Sha256HexArray Sha256Hex(std::string_view data, std::string_view secret) {
return ShaHex<ShaType::kSha256>(data, secret);
}

return BinToHex(std::span<const unsigned char>(binData, len));
Sha512HexArray Sha512Hex(std::string_view data, std::string_view secret) {
return ShaHex<ShaType::kSha512>(data, secret);
}

namespace {
using EVPMDCTXUniquePtr = std::unique_ptr<EVP_MD_CTX, decltype([](EVP_MD_CTX* ptr) { EVP_MD_CTX_free(ptr); })>;
using EVP_MD_CTX_UniquePtr = std::unique_ptr<EVP_MD_CTX, decltype([](EVP_MD_CTX* ptr) { EVP_MD_CTX_free(ptr); })>;

EVPMDCTXUniquePtr InitEVPMDCTXUniquePtr(ShaType shaType) {
EVPMDCTXUniquePtr mdctx(EVP_MD_CTX_new());
EVP_MD_CTX_UniquePtr CreateEVP_MD_CTX_UniquePtr(ShaType shaType) {
EVP_MD_CTX_UniquePtr mdCtx(EVP_MD_CTX_new());

EVP_DigestInit_ex(mdctx.get(), GetEVPMD(shaType), nullptr);
EVP_DigestInit_ex(mdCtx.get(), GetEVP_MD(shaType), nullptr);

return mdctx;
return mdCtx;
}

string EVPBinToHex(const EVPMDCTXUniquePtr& mdctx) {
unsigned int len = 0;
unsigned char binData[EVP_MAX_MD_SIZE];
template <ShaType shaType>
auto EVPBinToHex(const EVP_MD_CTX_UniquePtr& mdCtx) {
static constexpr unsigned int kExpectedLen = ShaDigestLen(shaType);

EVP_DigestFinal_ex(mdctx.get(), binData, &len);
unsigned int len = kExpectedLen;
std::array<char, kExpectedLen> binData;

EVP_DigestFinal_ex(mdCtx.get(), reinterpret_cast<unsigned char*>(binData.data()), &len);

if (len != kExpectedLen) {
throw exception("Unexpected result from EVP_DigestFinal_ex: expected len {}, got {}", kExpectedLen, len);
}

return BinToHex(std::span<const unsigned char>(binData, len));
return BinToLowerHex(binData);
}
} // namespace

string ShaDigest(ShaType shaType, std::string_view data) {
EVPMDCTXUniquePtr mdctx = InitEVPMDCTXUniquePtr(shaType);
template <ShaType shaType>
auto ShaDigest(std::string_view data) {
auto mdCtx = CreateEVP_MD_CTX_UniquePtr(shaType);

EVP_DigestUpdate(mdctx.get(), data.data(), data.size());
EVP_DigestUpdate(mdCtx.get(), data.data(), data.size());

return EVPBinToHex(mdctx);
return EVPBinToHex<shaType>(mdCtx);
}

string ShaDigest(ShaType shaType, std::span<const string> data) {
EVPMDCTXUniquePtr mdctx = InitEVPMDCTXUniquePtr(shaType);
template <ShaType shaType>
auto ShaDigest(std::span<const std::string_view> data) {
auto mdCtx = CreateEVP_MD_CTX_UniquePtr(shaType);

std::ranges::for_each(data, [&](std::string_view str) { EVP_DigestUpdate(mdctx.get(), str.data(), str.size()); });
std::ranges::for_each(data, [&](std::string_view str) { EVP_DigestUpdate(mdCtx.get(), str.data(), str.size()); });

return EVPBinToHex(mdctx);
return EVPBinToHex<shaType>(mdCtx);
}
} // namespace

Sha256DigestArray Sha256Digest(std::string_view data) { return ShaDigest<ShaType::kSha256>(data); }

Sha512DigestArray Sha512Digest(std::string_view data) { return ShaDigest<ShaType::kSha512>(data); }

Sha256DigestArray Sha256Digest(std::span<const std::string_view> data) { return ShaDigest<ShaType::kSha256>(data); }

Sha512DigestArray Sha512Digest(std::span<const std::string_view> data) { return ShaDigest<ShaType::kSha512>(data); }

} // namespace cct::ssl
22 changes: 10 additions & 12 deletions src/api/common/test/ssl_sha_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@
#include <algorithm>
#include <iterator>

#include "cct_string.hpp"

namespace cct::ssl {
TEST(SSLTest, Version) { EXPECT_NE(GetOpenSSLVersion(), ""); }

Expand All @@ -21,15 +19,15 @@ TEST(SSLTest, Sha256) {
}

TEST(SSLTest, ShaBin256) {
auto actual = ShaBin(ShaType::kSha256, "data1234", "secret1234");
auto actual = Sha256Bin("data1234", "secret1234");
static constexpr char kExpectedData[] = {11, -51, -56, -21, -101, 61, 35, 28, 86, 97, -50,
-8, 47, -113, -13, -107, -100, -93, 27, 71, 101, -128,
-65, 101, -110, -123, 38, 73, 77, 73, -10, -39};
EXPECT_TRUE(std::equal(actual.begin(), actual.end(), std::begin(kExpectedData), std::end(kExpectedData)));
}

TEST(SSLTest, ShaBin512) {
auto actual = ShaBin(ShaType::kSha512, "data1234", "secret1234");
auto actual = Sha512Bin("data1234", "secret1234");
static constexpr char kExpectedData[] = {-22, 39, 95, -39, -44, 39, 97, -40, 29, -120, -125, 84, -112,
-5, 69, -111, 3, -109, 86, 54, -31, 44, -55, 56, 111, 85,
87, 22, -61, 82, 89, 52, 105, 2, -89, -76, 63, 4, 95,
Expand All @@ -39,7 +37,7 @@ TEST(SSLTest, ShaBin512) {
}

TEST(SSLTest, ShaHex256) {
auto actual = ShaHex(ShaType::kSha256, "data1234", "secret1234");
auto actual = Sha256Hex("data1234", "secret1234");
static constexpr char kExpectedData[] = {48, 98, 99, 100, 99, 56, 101, 98, 57, 98, 51, 100, 50, 51, 49, 99,
53, 54, 54, 49, 99, 101, 102, 56, 50, 102, 56, 102, 102, 51, 57, 53,
57, 99, 97, 51, 49, 98, 52, 55, 54, 53, 56, 48, 98, 102, 54, 53,
Expand All @@ -48,7 +46,7 @@ TEST(SSLTest, ShaHex256) {
}

TEST(SSLTest, ShaHex512) {
auto actual = ShaHex(ShaType::kSha512, "data1234", "secret1234");
auto actual = Sha512Hex("data1234", "secret1234");
static constexpr char kExpectedData[] = {
101, 97, 50, 55, 53, 102, 100, 57, 100, 52, 50, 55, 54, 49, 100, 56, 49, 100, 56, 56, 56, 51,
53, 52, 57, 48, 102, 98, 52, 53, 57, 49, 48, 51, 57, 51, 53, 54, 51, 54, 101, 49, 50, 99,
Expand All @@ -60,7 +58,7 @@ TEST(SSLTest, ShaHex512) {
}

TEST(SSLTest, ShaDigest256) {
auto actual = ShaDigest(ShaType::kSha256, "data1234");
auto actual = Sha256Digest("data1234");
static constexpr char kExpectedData[] = {102, 50, 102, 100, 97, 57, 98, 98, 53, 49, 49, 56, 100, 100, 53, 97,
51, 50, 57, 55, 100, 50, 56, 97, 52, 55, 50, 57, 51, 102, 49, 50,
51, 97, 49, 51, 50, 54, 50, 57, 48, 101, 102, 51, 100, 55, 48, 49,
Expand All @@ -69,7 +67,7 @@ TEST(SSLTest, ShaDigest256) {
}

TEST(SSLTest, ShaDigest512) {
auto actual = ShaDigest(ShaType::kSha512, "data1234");
auto actual = Sha512Digest("data1234");
static constexpr char kExpectedData[] = {
99, 97, 97, 48, 50, 55, 54, 50, 57, 52, 98, 54, 49, 53, 48, 50, 51, 100, 57, 55, 50, 54,
48, 52, 55, 53, 50, 54, 98, 49, 50, 52, 102, 100, 99, 100, 51, 49, 98, 100, 97, 101, 97, 56,
Expand All @@ -81,9 +79,9 @@ TEST(SSLTest, ShaDigest512) {
}

TEST(SSLTest, ShaDigest256Multiple) {
static const string kData[] = {"data1234", "anotherString5_-", "5_0(7)fbBBBb334G;"};
static constexpr std::string_view kData[] = {"data1234", "anotherString5_-", "5_0(7)fbBBBb334G;"};

auto actual = ShaDigest(ShaType::kSha256, kData);
auto actual = Sha256Digest(kData);
static constexpr char kExpectedData[] = {53, 53, 100, 98, 52, 97, 49, 97, 50, 99, 52, 52, 52, 99, 97, 57,
100, 57, 97, 52, 48, 99, 51, 52, 101, 97, 50, 99, 53, 98, 97, 51,
100, 54, 55, 50, 102, 100, 51, 102, 100, 98, 51, 54, 52, 100, 98, 50,
Expand All @@ -92,9 +90,9 @@ TEST(SSLTest, ShaDigest256Multiple) {
}

TEST(SSLTest, ShaDigest512Multiple) {
static const string kData[] = {"data1234", "anotherString5_-", "5_0(7)fbBBBb334G;"};
static constexpr std::string_view kData[] = {"data1234", "anotherString5_-", "5_0(7)fbBBBb334G;"};

auto actual = ShaDigest(ShaType::kSha512, kData);
auto actual = Sha512Digest(kData);
static constexpr char kExpectedData[] = {
101, 56, 101, 55, 54, 98, 100, 56, 57, 53, 100, 53, 54, 99, 50, 54, 48, 56, 50, 57, 53, 97,
100, 98, 100, 48, 55, 102, 51, 56, 49, 54, 99, 55, 99, 101, 101, 98, 54, 48, 53, 52, 100, 98,
Expand Down
4 changes: 3 additions & 1 deletion src/api/exchanges/src/binanceprivateapi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,9 @@ void SetNonceAndSignature(const APIKey& apiKey, CurlPostData& postData, Duration

static constexpr std::string_view kSignatureKey = "signature";

postData.set_back(kSignatureKey, ssl::ShaHex(ssl::ShaType::kSha256, postData.str(), apiKey.privateKey()));
auto sha256Hex = ssl::Sha256Hex(postData.str(), apiKey.privateKey());

postData.set_back(kSignatureKey, std::string_view(sha256Hex));
}

bool CheckErrorDoRetry(int statusCode, const json& ret, QueryDelayDir& queryDelayDir, Duration& sleepingTime,
Expand Down
Loading

0 comments on commit 1234a5f

Please sign in to comment.