#include "challenge.hpp" #include "openssl_utils.hpp" #include #include #include #include #include #include Challenge::Challenge(Ref key, Connection & connection) : key_{std::move(key)} , connection_{connection} {} 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: slot_.disconnect(); BOOST_LOG_TRIVIAL(error) << "Challenge: No oper host"; break; case IrcCommand::RPL_YOUREOPER: slot_.disconnect(); connection_.send_ping("mitigation"); break; case IrcCommand::RPL_ENDOFRSACHALLENGE2: finish_challenge(); break; } } auto Challenge::finish_challenge() -> void { unsigned int digestlen = EVP_MAX_MD_SIZE; unsigned char digest[EVP_MAX_MD_SIZE]; size_t len = mybase64::decoded_size(buffer_.size()); std::vector ciphertext(len, 0); if (not mybase64::decode(buffer_, reinterpret_cast(ciphertext.data()), &len)) return log_openssl_errors("Challenge base64::decode: "); ciphertext.resize(len); // Setup decryption context Ref ctx{EVP_PKEY_CTX_new(key_.get(), nullptr)}; if (not ctx) return log_openssl_errors("Challenge EVP_PKEY_CTX_new: "); if (1 != EVP_PKEY_decrypt_init(ctx.get())) return log_openssl_errors("Challenge EVP_PKEY_decrypt_init: "); if (0 >= EVP_PKEY_CTX_set_rsa_padding(ctx.get(), RSA_PKCS1_OAEP_PADDING)) return log_openssl_errors("Challenge EVP_PKEY_CTX_set_rsa_padding: "); // Determine output size if (1 != EVP_PKEY_decrypt(ctx.get(), nullptr, &len, ciphertext.data(), ciphertext.size())) return log_openssl_errors("Challenge EVP_PKEY_decrypt (size): "); buffer_.resize(len); // Decrypt ciphertext if (1 != EVP_PKEY_decrypt(ctx.get(), reinterpret_cast(buffer_.data()), &len, ciphertext.data(), ciphertext.size())) return log_openssl_errors("Challenge EVP_PKEY_decrypt: "); buffer_.resize(len); // Hash the decrypted message if (1 != EVP_Digest(buffer_.data(), buffer_.size(), digest, &digestlen, EVP_sha1(), nullptr)) return log_openssl_errors("Challenge EVP_Digest: "); // 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_); buffer_.clear(); } auto Challenge::start(Connection &connection, const std::string_view user, Ref ref) -> std::shared_ptr { auto self = std::make_shared(std::move(ref), connection); self->slot_ = connection.sig_ircmsg.connect([self](auto cmd, auto &msg) { self->on_ircmsg(cmd, msg); }); connection.send_challenge(user); return self; }