2024-09-14 16:38:45 +00:00
|
|
|
#include <openssl/evp.h>
|
|
|
|
#include <openssl/hmac.h>
|
|
|
|
#include <openssl/rand.h>
|
|
|
|
#include <openssl/sha.h>
|
2024-09-26 18:11:30 +00:00
|
|
|
#include <spdlog/spdlog.h>
|
2024-09-14 16:38:45 +00:00
|
|
|
|
|
|
|
#include <boost/algorithm/string.hpp>
|
|
|
|
#include <boost/archive/iterators/base64_from_binary.hpp>
|
|
|
|
#include <boost/archive/iterators/binary_from_base64.hpp>
|
|
|
|
#include <boost/archive/iterators/transform_width.hpp>
|
|
|
|
#include <larra/encryption.hpp>
|
|
|
|
#include <random>
|
|
|
|
#include <ranges>
|
|
|
|
|
|
|
|
namespace larra::xmpp {
|
|
|
|
|
|
|
|
auto EncodeBase64(std::string_view val) -> std::string {
|
|
|
|
using namespace boost::archive::iterators; // NOLINT
|
|
|
|
using It = base64_from_binary<transform_width<std::string_view::const_iterator, 6, 8>>; // NOLINT
|
|
|
|
auto tmp = std::string(It(std::begin(val)), It(std::end(val)));
|
|
|
|
return tmp.append((3 - val.size() % 3) % 3, '=');
|
|
|
|
}
|
|
|
|
|
|
|
|
auto DecodeBase64(std::string_view val) -> std::string {
|
|
|
|
using namespace boost::archive::iterators; // NOLINT
|
|
|
|
using It = transform_width<binary_from_base64<std::string_view::const_iterator>, 8, 6>; // NOLINT
|
|
|
|
return boost::algorithm::trim_right_copy_if(std::string(It(std::begin(val)), It(std::end(val))), [](char c) {
|
|
|
|
return c == '\0';
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
auto GenerateNonce(std::size_t length) -> std::string { // NOLINT
|
|
|
|
constexpr std::string_view characters =
|
|
|
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
|
|
"abcdefghijklmnopqrstuvwxyz"
|
|
|
|
"0123456789";
|
|
|
|
|
|
|
|
std::random_device rd;
|
|
|
|
std::mt19937 generator(rd());
|
|
|
|
std::uniform_int_distribution<> distribution(0, characters.size() - 1);
|
|
|
|
|
|
|
|
return std::views::iota(std::size_t{}, length) | std::views::transform([&](...) {
|
|
|
|
return characters[distribution(generator)];
|
|
|
|
}) |
|
|
|
|
std::ranges::to<std::string>();
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename A, typename B, typename C>
|
|
|
|
struct DataHolder {
|
|
|
|
A first;
|
|
|
|
B second;
|
|
|
|
C third;
|
|
|
|
};
|
|
|
|
|
|
|
|
consteval auto GetDataForTag(sha512sum::EncryptionTag) {
|
|
|
|
return DataHolder{[] {
|
|
|
|
return EVP_sha512();
|
|
|
|
},
|
|
|
|
kSha512ResultSize,
|
|
|
|
[](auto... args) {
|
|
|
|
return SHA512(args...);
|
|
|
|
}};
|
|
|
|
}
|
|
|
|
|
|
|
|
consteval auto GetDataForTag(sha256sum::EncryptionTag) {
|
|
|
|
return DataHolder{[] {
|
|
|
|
return EVP_sha256();
|
|
|
|
},
|
|
|
|
kSha256ResultSize,
|
|
|
|
[](auto... args) {
|
|
|
|
return SHA256(args...);
|
|
|
|
}};
|
|
|
|
}
|
|
|
|
|
|
|
|
consteval auto GetDataForTag(sha1sum::EncryptionTag) {
|
|
|
|
return DataHolder{[] {
|
|
|
|
return EVP_sha256();
|
|
|
|
},
|
|
|
|
kSha1ResultSize,
|
|
|
|
[](auto... args) {
|
|
|
|
return SHA1(args...);
|
|
|
|
}};
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename TagType>
|
|
|
|
inline auto HashImpl(UnsignedStringView input, TagType) -> UnsignedString {
|
|
|
|
constexpr auto Size = GetDataForTag(TagType{}).second;
|
|
|
|
constexpr auto F = GetDataForTag(TagType{}).third;
|
|
|
|
UnsignedString response;
|
|
|
|
response.resize(Size);
|
|
|
|
F(input.data(), input.size(), response.data());
|
|
|
|
return response;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename TagType>
|
|
|
|
inline auto HmacImpl(std::string_view key, UnsignedStringView message, TagType) -> UnsignedString {
|
|
|
|
constexpr auto Size = GetDataForTag(TagType{}).second;
|
|
|
|
constexpr auto F = GetDataForTag(TagType{}).first;
|
|
|
|
UnsignedString result;
|
|
|
|
result.resize(Size);
|
|
|
|
unsigned int result_len{};
|
|
|
|
HMAC(F(), key.data(), static_cast<int>(key.size()), message.data(), message.size(), result.data(), &result_len);
|
|
|
|
result.resize(result_len);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename TagType>
|
|
|
|
inline auto Pbkdf2Impl(std::string_view password, UnsignedStringView salt, int iterations, TagType) -> UnsignedString {
|
|
|
|
constexpr auto Size = GetDataForTag(TagType{}).second;
|
|
|
|
constexpr auto F = GetDataForTag(TagType{}).first;
|
|
|
|
UnsignedString response;
|
|
|
|
response.resize(Size);
|
|
|
|
PKCS5_PBKDF2_HMAC(password.data(), password.length(), salt.data(), salt.size(), iterations, F(), Size, response.data()); // NOLINT
|
|
|
|
return response;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline auto ToCharStringView(std::ranges::range auto& str) -> std::string_view {
|
|
|
|
return {new(&*str.begin()) char[str.size()], str.size()};
|
|
|
|
}
|
|
|
|
|
|
|
|
inline auto ToUnsignedCharStringView(std::ranges::range auto& str) -> UnsignedStringView {
|
|
|
|
return {new(str.data()) unsigned char[str.size()], str.size()};
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename TagType>
|
|
|
|
inline auto GenerateAuthScramMessageImpl(std::string_view password,
|
|
|
|
std::string salt,
|
|
|
|
std::string_view serverNonce,
|
|
|
|
std::string_view firstServerMessage,
|
|
|
|
std::string_view initialMessage,
|
|
|
|
int iterations,
|
|
|
|
TagType tag) -> std::string {
|
2024-09-26 18:11:30 +00:00
|
|
|
SPDLOG_TRACE(
|
|
|
|
"Call function GenerateScramAuthMessage with params: [password: ({}), salt: Base64({}), serverNonce: ({}), firstServerMessage: ({}), "
|
|
|
|
"initialMessage: ({}), iterations: ({}), tag: ({})]",
|
|
|
|
password,
|
|
|
|
EncodeBase64(salt),
|
|
|
|
serverNonce,
|
|
|
|
firstServerMessage,
|
|
|
|
initialMessage,
|
|
|
|
iterations,
|
|
|
|
ToString(tag));
|
2024-09-14 16:38:45 +00:00
|
|
|
auto clientFinalMessageBare = std::format("c=biws,r={}", serverNonce);
|
2024-09-26 18:11:30 +00:00
|
|
|
SPDLOG_DEBUG("clientFinalMessageBare: ({})", clientFinalMessageBare);
|
|
|
|
auto saltedPassword = Pbdkf2(password, ToUnsignedCharStringView(salt), iterations, tag);
|
|
|
|
SPDLOG_DEBUG("saltedPassword: Base64({})", EncodeBase64(ToCharStringView(saltedPassword)));
|
2024-09-14 16:38:45 +00:00
|
|
|
std::string clientKeyStr = "Client Key"; // NOLINT
|
2024-09-26 18:11:30 +00:00
|
|
|
auto clientKey = Hmac(ToCharStringView(saltedPassword), ToUnsignedCharStringView(clientKeyStr), tag);
|
|
|
|
SPDLOG_DEBUG("clientKey: Base64({})", EncodeBase64(ToCharStringView(clientKey)));
|
|
|
|
auto storedKey = Hash(ToUnsignedCharStringView(clientKey), tag);
|
|
|
|
SPDLOG_DEBUG("storedKey: Base64({})", EncodeBase64(ToCharStringView(storedKey)));
|
2024-09-14 16:38:45 +00:00
|
|
|
auto authMessage = std::format("{},{},{}", initialMessage, firstServerMessage, clientFinalMessageBare);
|
2024-09-26 18:11:30 +00:00
|
|
|
SPDLOG_DEBUG("authMessage: ({})", authMessage);
|
|
|
|
auto clientSignature = Hmac(ToCharStringView(storedKey), ToUnsignedCharStringView(authMessage), tag);
|
|
|
|
SPDLOG_DEBUG("clientSignature: Base64({})", EncodeBase64(ToCharStringView(clientSignature)));
|
|
|
|
auto clientProof = std::views::zip(clientKey, ToUnsignedCharStringView(clientSignature)) | std::views::transform([&](auto arg) {
|
2024-09-15 20:50:01 +00:00
|
|
|
return std::get<0>(arg) ^ std::get<1>(arg);
|
2024-09-14 16:38:45 +00:00
|
|
|
}) |
|
|
|
|
std::ranges::to<std::string>();
|
2024-09-26 18:11:30 +00:00
|
|
|
auto clientProofBase64 = EncodeBase64(clientProof);
|
|
|
|
SPDLOG_DEBUG("clientProof: Base64({})", clientProofBase64);
|
|
|
|
return std::format("{},p={}", clientFinalMessageBare, clientProofBase64);
|
2024-09-14 16:38:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
namespace sha512sum {
|
|
|
|
|
2024-09-26 18:11:30 +00:00
|
|
|
auto ToString(const EncryptionTag&) -> std::string {
|
|
|
|
return "sha512sum";
|
|
|
|
}
|
|
|
|
|
2024-09-14 16:38:45 +00:00
|
|
|
auto Pbdkf2(std::string_view password, UnsignedStringView salt, int iterations, EncryptionTag tag) -> UnsignedString {
|
|
|
|
return Pbkdf2Impl(password, salt, iterations, tag);
|
|
|
|
}
|
|
|
|
|
|
|
|
auto Hash(UnsignedStringView data, EncryptionTag tag) -> UnsignedString {
|
|
|
|
return HashImpl(data, tag);
|
|
|
|
}
|
|
|
|
|
|
|
|
auto Hmac(std::string_view key, UnsignedStringView message, EncryptionTag tag) -> UnsignedString {
|
|
|
|
return HmacImpl(key, message, tag);
|
|
|
|
}
|
|
|
|
|
|
|
|
auto GenerateScramAuthMessage(std::string_view password,
|
|
|
|
std::string salt,
|
|
|
|
std::string_view serverNonce,
|
|
|
|
std::string_view firstServerMessage,
|
|
|
|
std::string_view initialMessage,
|
|
|
|
int iterations,
|
|
|
|
EncryptionTag tag) -> std::string {
|
|
|
|
return GenerateAuthScramMessageImpl(password, std::move(salt), serverNonce, firstServerMessage, initialMessage, iterations, tag);
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace sha512sum
|
|
|
|
|
|
|
|
namespace sha256sum {
|
|
|
|
|
2024-09-26 18:11:30 +00:00
|
|
|
auto ToString(const EncryptionTag&) -> std::string {
|
|
|
|
return "sha256sum";
|
|
|
|
}
|
|
|
|
|
2024-09-14 16:38:45 +00:00
|
|
|
auto Pbdkf2(std::string_view password, UnsignedStringView salt, int iterations, EncryptionTag tag) -> UnsignedString {
|
|
|
|
return Pbkdf2Impl(password, salt, iterations, tag);
|
|
|
|
}
|
|
|
|
|
|
|
|
auto Hash(UnsignedStringView data, EncryptionTag tag) -> UnsignedString {
|
|
|
|
return HashImpl(data, tag);
|
|
|
|
}
|
|
|
|
|
|
|
|
auto Hmac(std::string_view key, UnsignedStringView message, EncryptionTag tag) -> UnsignedString {
|
|
|
|
return HmacImpl(key, message, tag);
|
|
|
|
}
|
|
|
|
|
|
|
|
auto GenerateScramAuthMessage(std::string_view password,
|
|
|
|
std::string salt,
|
|
|
|
std::string_view serverNonce,
|
|
|
|
std::string_view firstServerMessage,
|
|
|
|
std::string_view initialMessage,
|
|
|
|
int iterations,
|
|
|
|
EncryptionTag tag) -> std::string {
|
|
|
|
return GenerateAuthScramMessageImpl(password, std::move(salt), serverNonce, firstServerMessage, initialMessage, iterations, tag);
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace sha256sum
|
|
|
|
|
|
|
|
namespace sha1sum {
|
|
|
|
|
2024-09-26 18:11:30 +00:00
|
|
|
auto ToString(const EncryptionTag&) -> std::string {
|
|
|
|
return "sha1sum";
|
|
|
|
}
|
|
|
|
|
2024-09-14 16:38:45 +00:00
|
|
|
auto Pbdkf2(std::string_view password, UnsignedStringView salt, int iterations, EncryptionTag tag) -> UnsignedString {
|
|
|
|
return Pbkdf2Impl(password, salt, iterations, tag);
|
|
|
|
}
|
|
|
|
|
|
|
|
auto Hash(UnsignedStringView data, EncryptionTag tag) -> UnsignedString {
|
|
|
|
return HashImpl(data, tag);
|
|
|
|
}
|
|
|
|
|
|
|
|
auto Hmac(std::string_view key, UnsignedStringView message, EncryptionTag tag) -> UnsignedString {
|
|
|
|
return HmacImpl(key, message, tag);
|
|
|
|
}
|
|
|
|
|
|
|
|
auto GenerateScramAuthMessage(std::string_view password,
|
|
|
|
std::string salt,
|
|
|
|
std::string_view serverNonce,
|
|
|
|
std::string_view firstServerMessage,
|
|
|
|
std::string_view initialMessage,
|
|
|
|
int iterations,
|
|
|
|
EncryptionTag tag) -> std::string {
|
|
|
|
return GenerateAuthScramMessageImpl(password, std::move(salt), serverNonce, firstServerMessage, initialMessage, iterations, tag);
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace sha1sum
|
|
|
|
|
|
|
|
} // namespace larra::xmpp
|