From 3a7c3c0fe99df82f464bdeb4144d10d351ed68ff Mon Sep 17 00:00:00 2001 From: Martin Michelsen Date: Fri, 2 Sep 2022 17:29:19 -0700 Subject: [PATCH] implement BB encryption in --cat-client --- src/CatSession.cc | 23 ++++++++++++++--------- src/CatSession.hh | 4 +++- src/Main.cc | 13 +++++++++++-- 3 files changed, 28 insertions(+), 12 deletions(-) diff --git a/src/CatSession.cc b/src/CatSession.cc index 4b8bc072..f9cd9691 100644 --- a/src/CatSession.cc +++ b/src/CatSession.cc @@ -36,7 +36,8 @@ using namespace std; CatSession::CatSession( shared_ptr base, const struct sockaddr_storage& remote, - GameVersion version) + GameVersion version, + shared_ptr bb_key_file) : Shell(base), log("[CatSession] ", proxy_server_log.min_level), channel( @@ -44,7 +45,8 @@ CatSession::CatSession( CatSession::dispatch_on_channel_input, CatSession::dispatch_on_channel_error, this, - "CatSession") { + "CatSession"), + bb_key_file(bb_key_file) { if (remote.ss_family != AF_INET) { throw runtime_error("remote is not AF_INET"); } @@ -91,13 +93,16 @@ void CatSession::on_channel_input( } } } else { // BB - // TODO: This can easily be done; I'm just lazy. We need to have the user - // pass in a key name, then get that key file from this->state, then create - // the crypts. - // TODO: We really should just move encryption handling into the Channel - // abstraction instead of the above, but this is a bit harder because then - // Channels would have to know about BB key files. - throw runtime_error("CatSession does not implement BB encryption yet"); + if (command == 0x03 || command == 0x9B) { + if (!this->bb_key_file) { + throw runtime_error("BB encryption requires a key file"); + } + const auto& cmd = check_size_t(data, + offsetof(S_ServerInit_DC_PC_V3_02_17_91_9B, after_message), 0xFFFF); + this->channel.crypt_in.reset(new PSOBBEncryption(*this->bb_key_file, &cmd.server_key[0], sizeof(cmd.server_key))); + this->channel.crypt_out.reset(new PSOBBEncryption(*this->bb_key_file, &cmd.client_key[0], sizeof(cmd.client_key))); + this->log.info("Enabled BB encryption"); + } } string full_cmd = prepend_command_header( diff --git a/src/CatSession.hh b/src/CatSession.hh index e69df6b0..01013831 100644 --- a/src/CatSession.hh +++ b/src/CatSession.hh @@ -23,12 +23,14 @@ public: CatSession( std::shared_ptr base, const struct sockaddr_storage& remote, - GameVersion version); + GameVersion version, + std::shared_ptr bb_key_file); virtual ~CatSession() = default; protected: PrefixedLogger log; Channel channel; + std::shared_ptr bb_key_file; virtual void print_prompt(); virtual void execute_command(const std::string& command); diff --git a/src/Main.cc b/src/Main.cc index c64bd504..393a7016 100644 --- a/src/Main.cc +++ b/src/Main.cc @@ -245,7 +245,8 @@ Specifically:\n\ commands to stdout, and forward any commands typed into stdin to the\n\ remote server. It is assumed that the input and output are terminals, so\n\ all commands are hex-encoded. The --patch, --dc, --pc, --gc, and --bb\n\ - options can be used to select the command format end encryption.\n\ + options can be used to select the command format and encryption. If --bb\n\ + is used, the --key option is also required (as in --decrypt-data above).\n\ --replay-log=FILENAME\n\ This option makes newserv replay terminal log as if it were a client\n\ session. This is used for regression testing, to make sure client\n\ @@ -438,8 +439,16 @@ int main(int argc, char** argv) { } case Behavior::CAT_CLIENT: { + shared_ptr key; + if (cli_version == GameVersion::BB) { + if (key_file_name.empty()) { + throw runtime_error("a key filename is required for BB client emulation"); + } + key.reset(new PSOBBEncryption::KeyFile( + load_object_file("system/blueburst/keys/" + key_file_name + ".nsk"))); + } shared_ptr base(event_base_new(), event_base_free); - CatSession session(base, cat_client_remote, cli_version); + CatSession session(base, cat_client_remote, cli_version, key); event_base_dispatch(base.get()); break; }