From de19233dd72bb28edabdf26b02a385592147c5a7 Mon Sep 17 00:00:00 2001 From: Eric Mertens Date: Tue, 28 Jan 2025 19:02:30 -0800 Subject: [PATCH] challenge.cpp --- challenge.cpp | 90 +++++++++++++++++++++++++++++++++++++++++++++++++++ challenge.hpp | 24 ++++++++++++++ 2 files changed, 114 insertions(+) create mode 100644 challenge.cpp create mode 100644 challenge.hpp diff --git a/challenge.cpp b/challenge.cpp new file mode 100644 index 0000000..9de6a64 --- /dev/null +++ b/challenge.cpp @@ -0,0 +1,90 @@ +#include "challenge.hpp" + +#include "openssl_errors.hpp" + +#include +#include +#include + +#include + +#include +#include + +Challenge::Challenge(EVP_PKEY_Ref key, Connection & connection) + : key_{std::move(key)} + , connection_{connection} + {} + + +auto Challenge::stop() -> void +{ + slot_.disconnect(); + buffer_.clear(); +} + +auto Challenge::on_ircmsg(IrcCommand cmd, const IrcMsg &msg) -> void { + switch (cmd) { + default: break; + case IrcCommand::RPL_RSACHALLENGE2: + buffer_ += msg.args[1]; + break; + case IrcCommand::ERR_NOOPERHOST: + BOOST_LOG_TRIVIAL(error) << "Challenge: No oper host"; + stop(); + break; + case IrcCommand::RPL_YOUREOPER: + BOOST_LOG_TRIVIAL(error) << "Challenge: Already oper"; + stop(); + break; + case IrcCommand::RPL_ENDOFRSACHALLENGE2: + { + EVP_PKEY_CTX_Ref ctx; + unsigned int digestlen = 160; + unsigned char digest[160]; + size_t len = mybase64::decoded_size(buffer_.size()); + std::vector ciphertext(len, 0); + + if (not mybase64::decode(buffer_, reinterpret_cast(ciphertext.data()), &len)) goto error; + ciphertext.resize(len); + + // Setup decryption context + ctx.reset(EVP_PKEY_CTX_new(key_.get(), nullptr)); + if (ctx.get() == nullptr) goto error; + if (1 != EVP_PKEY_decrypt_init(ctx.get())) goto error; + if (0 <= EVP_PKEY_CTX_set_rsa_padding(ctx.get(), RSA_PKCS1_OAEP_PADDING)) goto error; + + // Determine output size + if (1 != EVP_PKEY_decrypt(ctx.get(), nullptr, &len, ciphertext.data(), ciphertext.size())) goto error; + buffer_.resize(len); + + // Decrypt ciphertext + EVP_PKEY_decrypt(ctx.get(), reinterpret_cast(buffer_.data()), &len, ciphertext.data(), ciphertext.size()); + + // Hash the decrypted message + if (1 != EVP_Digest(buffer_.data(), buffer_.size(), digest, &digestlen, EVP_sha1(), nullptr)) goto error; + + // Construct reply as '+' and base64 encoded digest + buffer_.resize(mybase64::encoded_size(digestlen) + 1); + buffer_[0] = '+'; + mybase64::encode(std::string_view{(char*)digest, digestlen}, buffer_.data() + 1); + + connection_.send_challenge(buffer_); + + stop(); + return; + +error: + log_openssl_errors("Challenge: "); + stop(); + } + } + } + +auto Challenge::start(Connection &connection, const std::string_view user, EVP_PKEY_Ref ref) -> std::shared_ptr +{ + auto self = std::make_shared(std::move(ref), connection); + connection.sig_ircmsg.connect([self](auto cmd, auto &msg) { self->on_ircmsg(cmd, msg); }); + connection.send_challenge(user); + return self; +} diff --git a/challenge.hpp b/challenge.hpp new file mode 100644 index 0000000..800b5f2 --- /dev/null +++ b/challenge.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include "connection.hpp" +#include "ref.hpp" + +#include + +#include +#include + +class Challenge : std::enable_shared_from_this +{ + EVP_PKEY_Ref key_; + Connection &connection_; + boost::signals2::scoped_connection slot_; + std::string buffer_; + + auto on_ircmsg(IrcCommand cmd, const IrcMsg &msg) -> void; + +public: + Challenge(EVP_PKEY_Ref, Connection &); + auto stop() -> void; + static auto start(Connection &, std::string_view user, EVP_PKEY_Ref) -> std::shared_ptr; +};