Move all encryption to separate file
This commit is contained in:
parent
3b78412da4
commit
4060a42634
3 changed files with 337 additions and 125 deletions
|
@ -1,13 +1,4 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <openssl/evp.h>
|
|
||||||
#include <openssl/hmac.h>
|
|
||||||
#include <openssl/rand.h>
|
|
||||||
#include <openssl/sha.h>
|
|
||||||
|
|
||||||
#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 <boost/asio/awaitable.hpp>
|
#include <boost/asio/awaitable.hpp>
|
||||||
#include <boost/asio/connect.hpp>
|
#include <boost/asio/connect.hpp>
|
||||||
#include <boost/asio/ip/tcp.hpp>
|
#include <boost/asio/ip/tcp.hpp>
|
||||||
|
@ -17,10 +8,10 @@
|
||||||
#include <boost/asio/use_awaitable.hpp>
|
#include <boost/asio/use_awaitable.hpp>
|
||||||
#include <charconv>
|
#include <charconv>
|
||||||
#include <larra/client/options.hpp>
|
#include <larra/client/options.hpp>
|
||||||
|
#include <larra/encryption.hpp>
|
||||||
#include <larra/features.hpp>
|
#include <larra/features.hpp>
|
||||||
#include <larra/stream.hpp>
|
#include <larra/stream.hpp>
|
||||||
#include <larra/user_account.hpp>
|
#include <larra/user_account.hpp>
|
||||||
#include <random>
|
|
||||||
#include <ranges>
|
#include <ranges>
|
||||||
namespace larra::xmpp {
|
namespace larra::xmpp {
|
||||||
|
|
||||||
|
@ -51,76 +42,6 @@ struct ServerRequiresStartTls : std::exception {
|
||||||
|
|
||||||
namespace impl {
|
namespace impl {
|
||||||
|
|
||||||
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 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, '=');
|
|
||||||
}
|
|
||||||
|
|
||||||
struct CharTrait {
|
|
||||||
using char_type = unsigned char;
|
|
||||||
static constexpr auto assign(char_type& c1, const char_type& c2) -> void {
|
|
||||||
c1 = c2;
|
|
||||||
}
|
|
||||||
static constexpr auto assign(char_type* c1, std::size_t n, const char_type c2) -> char_type* {
|
|
||||||
std::ranges::fill_n(c1, static_cast<std::iter_difference_t<unsigned char>>(n), c2);
|
|
||||||
return c1;
|
|
||||||
}
|
|
||||||
static constexpr auto copy(char_type* c1, const char_type* c2, std::size_t n) -> char_type* {
|
|
||||||
std::ranges::copy_n(c2, static_cast<std::iter_difference_t<unsigned char>>(n), c1);
|
|
||||||
return c1;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
inline auto GenerateNonce(std::size_t length = 24) -> 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);
|
|
||||||
|
|
||||||
std::ostringstream nonceStream;
|
|
||||||
for(size_t i = 0; i < length; ++i) {
|
|
||||||
nonceStream << characters[distribution(generator)];
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::move(nonceStream.str());
|
|
||||||
}
|
|
||||||
|
|
||||||
template <auto F = EVP_sha512, std::size_t Size = 64> // NOLINT
|
|
||||||
inline auto Pbkdf2(std::string_view password,
|
|
||||||
std::basic_string_view<unsigned char, CharTrait> salt,
|
|
||||||
int iterations) -> std::array<unsigned char, Size> {
|
|
||||||
std::array<unsigned char, Size> key;
|
|
||||||
PKCS5_PBKDF2_HMAC(password.data(), password.length(), salt.data(), salt.size(), iterations, F(), 64, key.data()); // NOLINT
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
template <auto F = SHA512, std::size_t Size = 64>
|
|
||||||
inline auto SHA(std::basic_string_view<unsigned char, CharTrait> input) -> std::array<unsigned char, Size> {
|
|
||||||
std::array<unsigned char, Size> response;
|
|
||||||
F(input.data(), input.size(), response.data());
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <auto F = EVP_sha512, std::size_t Size = 64>
|
|
||||||
inline auto HMAC(std::string_view key,
|
|
||||||
std::basic_string_view<unsigned char, CharTrait> message) -> std::basic_string<unsigned char, CharTrait> {
|
|
||||||
unsigned char* result = HMAC(F(), key.data(), static_cast<int>(key.size()), message.data(), message.size(), nullptr, nullptr);
|
|
||||||
return {result, Size};
|
|
||||||
}
|
|
||||||
|
|
||||||
inline auto StartStream(const BareJid& from, auto& connection) -> boost::asio::awaitable<void> {
|
inline auto StartStream(const BareJid& from, auto& connection) -> boost::asio::awaitable<void> {
|
||||||
auto stream = UserStream{}.To(from.server).From(std::move(from)).Version("1.0").XmlLang("en");
|
auto stream = UserStream{}.To(from.server).From(std::move(from)).Version("1.0").XmlLang("en");
|
||||||
auto buffer = "<?xml version='1.0'?>" + ToString(stream);
|
auto buffer = "<?xml version='1.0'?>" + ToString(stream);
|
||||||
|
@ -144,44 +65,6 @@ inline auto ToInt(std::string_view input) -> std::optional<int> {
|
||||||
return result.ec == std::errc::invalid_argument || result.ec == std::errc::result_out_of_range ? std::nullopt : std::optional{out};
|
return result.ec == std::errc::invalid_argument || result.ec == std::errc::result_out_of_range ? std::nullopt : std::optional{out};
|
||||||
}
|
}
|
||||||
|
|
||||||
auto ToCharStringView(std::ranges::range auto& str) -> std::string_view {
|
|
||||||
return {new(&*str.begin()) char[str.size()], str.size()};
|
|
||||||
}
|
|
||||||
|
|
||||||
inline auto ToUnsignedCharStringView(std::string& str) -> std::basic_string_view<unsigned char, CharTrait> {
|
|
||||||
return {new(str.data()) unsigned char[str.size()], str.size()};
|
|
||||||
}
|
|
||||||
|
|
||||||
auto Xor(auto str1, auto str2) -> auto {
|
|
||||||
if(str1.length() != str2.length()) {
|
|
||||||
throw std::invalid_argument("Strings must be of equal length for XOR.");
|
|
||||||
}
|
|
||||||
return std::views::iota(std::size_t{}, str1.size()) | std::views::transform([str1 = std::move(str1), str2 = std::move(str2)](auto i) {
|
|
||||||
return str1[i] ^ str2[i];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
template <auto F = EVP_sha512, auto F2 = SHA512, std::size_t Size = 64>
|
|
||||||
auto GenerateAuthScramMessage(std::string_view password,
|
|
||||||
std::string salt,
|
|
||||||
std::string_view serverNonce,
|
|
||||||
std::string_view firstServerMessage,
|
|
||||||
std::string_view initialMessage,
|
|
||||||
int iterations) -> std::string {
|
|
||||||
auto clientFinalMessageBare = std::format("c=biws,r={}", serverNonce);
|
|
||||||
auto saltedPassword = Pbkdf2<F, Size>(password, ToUnsignedCharStringView(salt), iterations);
|
|
||||||
std::string clientKeyStr = "Client Key"; // NOLINT
|
|
||||||
auto clientKey = HMAC<F, Size>(ToCharStringView(saltedPassword), ToUnsignedCharStringView(clientKeyStr));
|
|
||||||
auto storedKey = SHA<F2, Size>(clientKey);
|
|
||||||
auto authMessage = std::format("{},{},{}", initialMessage, firstServerMessage, clientFinalMessageBare);
|
|
||||||
auto clientSignature = HMAC<F, Size>(ToCharStringView(storedKey), ToUnsignedCharStringView(authMessage));
|
|
||||||
auto clientProof = Xor(clientKey, clientSignature) | std::ranges::to<std::string>();
|
|
||||||
std::string serverKeyStr = "Server Key";
|
|
||||||
auto serverKey = HMAC<F, Size>(ToCharStringView(saltedPassword), ToUnsignedCharStringView(serverKeyStr));
|
|
||||||
auto serverSignature = HMAC<F, Size>(ToCharStringView(serverKey), ToUnsignedCharStringView(authMessage));
|
|
||||||
return std::format("{},p={}", clientFinalMessageBare, EncodeBase64(ToCharStringView(clientProof)));
|
|
||||||
}
|
|
||||||
|
|
||||||
inline auto ParseChallenge(std::string_view str) {
|
inline auto ParseChallenge(std::string_view str) {
|
||||||
return std::views::split(str, ',') | std::views::transform([](auto param) {
|
return std::views::split(str, ',') | std::views::transform([](auto param) {
|
||||||
return std::string_view{param};
|
return std::string_view{param};
|
||||||
|
@ -221,8 +104,10 @@ struct ClientCreateVisitor {
|
||||||
co_await boost::asio::async_read_until(socket, boost::asio::dynamic_buffer(response), '>', boost::asio::use_awaitable);
|
co_await boost::asio::async_read_until(socket, boost::asio::dynamic_buffer(response), '>', boost::asio::use_awaitable);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <auto F, auto F2, std::size_t Size>
|
auto ScramAuth(std::string_view methodName,
|
||||||
auto ScramAuth(std::string_view methodName, const EncryptionUserAccount& account, auto& socket) -> boost::asio::awaitable<void> {
|
const EncryptionUserAccount& account,
|
||||||
|
auto& socket,
|
||||||
|
auto tag) -> boost::asio::awaitable<void> {
|
||||||
pugi::xml_document doc;
|
pugi::xml_document doc;
|
||||||
auto auth = doc.append_child("auth");
|
auto auth = doc.append_child("auth");
|
||||||
auth.append_attribute("xmlns") = "urn:ietf:params:xml:ns:xmpp-sasl";
|
auth.append_attribute("xmlns") = "urn:ietf:params:xml:ns:xmpp-sasl";
|
||||||
|
@ -252,8 +137,9 @@ struct ClientCreateVisitor {
|
||||||
auto success = doc.append_child("response");
|
auto success = doc.append_child("response");
|
||||||
success.append_attribute("xmlns") = "urn:ietf:params:xml:ns:xmpp-sasl";
|
success.append_attribute("xmlns") = "urn:ietf:params:xml:ns:xmpp-sasl";
|
||||||
success.text().set(
|
success.text().set(
|
||||||
EncodeBase64(GenerateAuthScramMessage<F, F2, Size>(
|
EncodeBase64(
|
||||||
account.password, DecodeBase64(params["s"]), serverNonce, decoded, initialMessage, ToInt(params["i"]).value()))
|
GenerateScramAuthMessage(
|
||||||
|
account.password, DecodeBase64(params["s"]), serverNonce, decoded, initialMessage, ToInt(params["i"]).value(), tag))
|
||||||
.c_str());
|
.c_str());
|
||||||
std::ostringstream strstream2;
|
std::ostringstream strstream2;
|
||||||
doc.print(strstream2,
|
doc.print(strstream2,
|
||||||
|
@ -277,13 +163,13 @@ struct ClientCreateVisitor {
|
||||||
ServerToUserStream stream) -> boost::asio::awaitable<void> {
|
ServerToUserStream stream) -> boost::asio::awaitable<void> {
|
||||||
// NOLINTBEGIN
|
// NOLINTBEGIN
|
||||||
if(std::ranges::contains(features.saslMechanisms.mechanisms, "SCRAM-SHA-512")) {
|
if(std::ranges::contains(features.saslMechanisms.mechanisms, "SCRAM-SHA-512")) {
|
||||||
co_return co_await ScramAuth<EVP_sha512, SHA512, 64>("SCRAM-SHA-512", account, socket);
|
co_return co_await ScramAuth("SCRAM-SHA-512", account, socket, sha512sum::EncryptionTag{});
|
||||||
}
|
}
|
||||||
if(std::ranges::contains(features.saslMechanisms.mechanisms, "SCRAM-SHA-256")) {
|
if(std::ranges::contains(features.saslMechanisms.mechanisms, "SCRAM-SHA-256")) {
|
||||||
co_return co_await ScramAuth<EVP_sha256, SHA256, 32>("SCRAM-SHA-256", account, socket);
|
co_return co_await ScramAuth("SCRAM-SHA-256", account, socket, sha256sum::EncryptionTag{});
|
||||||
}
|
}
|
||||||
if(std::ranges::contains(features.saslMechanisms.mechanisms, "SCRAM-SHA-1")) {
|
if(std::ranges::contains(features.saslMechanisms.mechanisms, "SCRAM-SHA-1")) {
|
||||||
co_return co_await ScramAuth<EVP_sha1, SHA1, 20>("SCRAM-SHA-1", account, socket);
|
co_return co_await ScramAuth("SCRAM-SHA-1", account, socket, sha1sum::EncryptionTag{});
|
||||||
}
|
}
|
||||||
// NOLINTEND
|
// NOLINTEND
|
||||||
throw std::runtime_error("Server not support SCRAM SHA 1 or SCRAM SHA 256 or SCRAM SHA 512 auth");
|
throw std::runtime_error("Server not support SCRAM SHA 1 or SCRAM SHA 256 or SCRAM SHA 512 auth");
|
||||||
|
|
98
library/include/larra/encryption.hpp
Normal file
98
library/include/larra/encryption.hpp
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
#pragma once
|
||||||
|
#include <algorithm>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace larra::xmpp {
|
||||||
|
|
||||||
|
constexpr std::size_t kSha512ResultSize = 64;
|
||||||
|
|
||||||
|
constexpr std::size_t kSha256ResultSize = 32;
|
||||||
|
|
||||||
|
constexpr std::size_t kSha1ResultSize = 20;
|
||||||
|
|
||||||
|
struct UnsignedCharTrait {
|
||||||
|
using char_type = unsigned char;
|
||||||
|
static constexpr auto assign(char_type& c1, const char_type& c2) -> void {
|
||||||
|
c1 = c2;
|
||||||
|
}
|
||||||
|
static constexpr auto assign(char_type* c1, std::size_t n, const char_type c2) -> char_type* {
|
||||||
|
std::ranges::fill_n(c1, static_cast<std::iter_difference_t<unsigned char>>(n), c2);
|
||||||
|
return c1;
|
||||||
|
}
|
||||||
|
static constexpr auto copy(char_type* c1, const char_type* c2, std::size_t n) -> char_type* {
|
||||||
|
std::ranges::copy_n(c2, static_cast<std::iter_difference_t<unsigned char>>(n), c1);
|
||||||
|
return c1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using UnsignedStringView = std::basic_string_view<unsigned char, UnsignedCharTrait>;
|
||||||
|
|
||||||
|
using UnsignedString = std::basic_string<unsigned char, UnsignedCharTrait>;
|
||||||
|
|
||||||
|
auto EncodeBase64(std::string_view) -> std::string;
|
||||||
|
|
||||||
|
auto DecodeBase64(std::string_view) -> std::string;
|
||||||
|
|
||||||
|
auto GenerateNonce(std::size_t length = 24) -> std::string; // NOLINT
|
||||||
|
|
||||||
|
namespace sha512sum {
|
||||||
|
|
||||||
|
struct EncryptionTag {};
|
||||||
|
|
||||||
|
auto Pbdkf2(std::string_view password, UnsignedStringView salt, int iterations, EncryptionTag = {}) -> UnsignedString;
|
||||||
|
|
||||||
|
auto Hash(UnsignedStringView data, EncryptionTag = {}) -> UnsignedString;
|
||||||
|
|
||||||
|
auto Hmac(std::string_view key, UnsignedStringView message, EncryptionTag = {}) -> UnsignedString;
|
||||||
|
|
||||||
|
auto GenerateScramAuthMessage(std::string_view password,
|
||||||
|
std::string salt,
|
||||||
|
std::string_view serverNonce,
|
||||||
|
std::string_view firstServerMessage,
|
||||||
|
std::string_view initialMessage,
|
||||||
|
int iterations,
|
||||||
|
EncryptionTag = {}) -> std::string;
|
||||||
|
|
||||||
|
} // namespace sha512sum
|
||||||
|
|
||||||
|
namespace sha256sum {
|
||||||
|
|
||||||
|
struct EncryptionTag {};
|
||||||
|
|
||||||
|
auto Pbdkf2(std::string_view password, UnsignedStringView salt, int iterations, EncryptionTag = {}) -> UnsignedString;
|
||||||
|
|
||||||
|
auto Hash(UnsignedStringView data, EncryptionTag = {}) -> UnsignedString;
|
||||||
|
|
||||||
|
auto Hmac(std::string_view key, UnsignedStringView message, EncryptionTag = {}) -> UnsignedString;
|
||||||
|
|
||||||
|
auto GenerateScramAuthMessage(std::string_view password,
|
||||||
|
std::string salt,
|
||||||
|
std::string_view serverNonce,
|
||||||
|
std::string_view firstServerMessage,
|
||||||
|
std::string_view initialMessage,
|
||||||
|
int iterations,
|
||||||
|
EncryptionTag = {}) -> std::string;
|
||||||
|
|
||||||
|
} // namespace sha256sum
|
||||||
|
|
||||||
|
namespace sha1sum {
|
||||||
|
|
||||||
|
struct EncryptionTag {};
|
||||||
|
|
||||||
|
auto Pbdkf2(std::string_view password, UnsignedStringView salt, int iterations, EncryptionTag = {}) -> UnsignedString;
|
||||||
|
|
||||||
|
auto Hash(UnsignedStringView data, EncryptionTag = {}) -> UnsignedString;
|
||||||
|
|
||||||
|
auto Hmac(std::string_view key, UnsignedStringView message, EncryptionTag = {}) -> UnsignedString;
|
||||||
|
|
||||||
|
auto GenerateScramAuthMessage(std::string_view password,
|
||||||
|
std::string salt,
|
||||||
|
std::string_view serverNonce,
|
||||||
|
std::string_view firstServerMessage,
|
||||||
|
std::string_view initialMessage,
|
||||||
|
int iterations,
|
||||||
|
EncryptionTag = {}) -> std::string;
|
||||||
|
|
||||||
|
} // namespace sha1sum
|
||||||
|
|
||||||
|
} // namespace larra::xmpp
|
228
library/src/encryption.cpp
Normal file
228
library/src/encryption.cpp
Normal file
|
@ -0,0 +1,228 @@
|
||||||
|
#include <openssl/evp.h>
|
||||||
|
#include <openssl/hmac.h>
|
||||||
|
#include <openssl/rand.h>
|
||||||
|
#include <openssl/sha.h>
|
||||||
|
|
||||||
|
#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 {
|
||||||
|
auto clientFinalMessageBare = std::format("c=biws,r={}", serverNonce);
|
||||||
|
auto saltedPassword = Pbkdf2Impl(password, ToUnsignedCharStringView(salt), iterations, tag);
|
||||||
|
std::string clientKeyStr = "Client Key"; // NOLINT
|
||||||
|
auto clientKey = HmacImpl(ToCharStringView(saltedPassword), ToUnsignedCharStringView(clientKeyStr), tag);
|
||||||
|
auto storedKey = HashImpl(clientKey, tag);
|
||||||
|
auto authMessage = std::format("{},{},{}", initialMessage, firstServerMessage, clientFinalMessageBare);
|
||||||
|
auto clientSignature = HmacImpl(ToCharStringView(storedKey), ToUnsignedCharStringView(authMessage), tag);
|
||||||
|
auto clientProof = std::views::iota(std::size_t{}, clientKey.size()) | // No std::views::enumerate in libc++
|
||||||
|
std::views::transform([&](auto i) {
|
||||||
|
return clientKey[i] ^ clientSignature[i];
|
||||||
|
}) |
|
||||||
|
std::ranges::to<std::string>();
|
||||||
|
std::string serverKeyStr = "Server Key";
|
||||||
|
auto serverKey = HmacImpl(ToCharStringView(saltedPassword), ToUnsignedCharStringView(serverKeyStr), tag);
|
||||||
|
auto serverSignature = HmacImpl(ToCharStringView(serverKey), ToUnsignedCharStringView(authMessage), tag);
|
||||||
|
return std::format("{},p={}", clientFinalMessageBare, EncodeBase64(ToCharStringView(clientProof)));
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace 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 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 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
|
Loading…
Reference in a new issue