#include #include #include #include #include #include #include #include #include #include #include #include namespace larra::xmpp { auto EncodeBase64(std::string_view val) -> std::string { using namespace boost::archive::iterators; // NOLINT using It = base64_from_binary>; // 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, 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(); } template struct DataHolder { A first; B second; C third; }; consteval auto GetDataForTag(sha512sum::EncryptionTag) { return DataHolder{.first = [] { return EVP_sha512(); }, .second = kSha512ResultSize, .third = [](auto... args) { return SHA512(args...); }}; } consteval auto GetDataForTag(sha256sum::EncryptionTag) { return DataHolder{.first = [] { return EVP_sha256(); }, .second = kSha256ResultSize, .third = [](auto... args) { return SHA256(args...); }}; } consteval auto GetDataForTag(sha1sum::EncryptionTag) { return DataHolder{.first = [] { return EVP_sha256(); }, .second = kSha1ResultSize, .third = [](auto... args) { return SHA1(args...); }}; } template 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 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(key.size()), message.data(), message.size(), result.data(), &result_len); result.resize(result_len); return result; } template 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 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 { SPDLOG_TRACE( "Call function GenerateScramAuthMessage with params: [password: ({}), salt: Base64({}), serverNonce: ({}), firstServerMessage: ({}), " "initialMessage: ({}), iterations: ({}), tag: ({})]", password, EncodeBase64(salt), serverNonce, firstServerMessage, initialMessage, iterations, ToString(tag)); auto clientFinalMessageBare = std::format("c=biws,r={}", serverNonce); SPDLOG_DEBUG("clientFinalMessageBare: ({})", clientFinalMessageBare); auto saltedPassword = Pbdkf2(password, ToUnsignedCharStringView(salt), iterations, tag); SPDLOG_DEBUG("saltedPassword: Base64({})", EncodeBase64(ToCharStringView(saltedPassword))); std::string clientKeyStr = "Client Key"; // NOLINT 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))); auto authMessage = std::format("{},{},{}", initialMessage, firstServerMessage, clientFinalMessageBare); 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) { return std::get<0>(arg) ^ std::get<1>(arg); }) | std::ranges::to(); auto clientProofBase64 = EncodeBase64(clientProof); SPDLOG_DEBUG("clientProof: Base64({})", clientProofBase64); return std::format("{},p={}", clientFinalMessageBare, clientProofBase64); } namespace sha512sum { auto ToString(const EncryptionTag&) -> std::string { return "sha512sum"; } 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 { auto ToString(const EncryptionTag&) -> std::string { return "sha256sum"; } 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 { auto ToString(const EncryptionTag&) -> std::string { return "sha1sum"; } 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