feat: add support for NoChatReports encryption

This commit is contained in:
Ryan 2025-03-04 20:23:41 -05:00
parent 426c19304d
commit c4454fe217
Signed by: ErrorNoInternet
GPG Key ID: 2486BFB7B1E6A4A3
9 changed files with 341 additions and 25 deletions

155
Cargo.lock generated
View File

@ -17,6 +17,16 @@ version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
[[package]]
name = "aead"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0"
dependencies = [
"crypto-common",
"generic-array",
]
[[package]]
name = "aes"
version = "0.8.4"
@ -28,6 +38,20 @@ dependencies = [
"cpufeatures",
]
[[package]]
name = "aes-gcm"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1"
dependencies = [
"aead",
"aes",
"cipher",
"ctr",
"ghash",
"subtle",
]
[[package]]
name = "ahash"
version = "0.8.11"
@ -882,6 +906,15 @@ dependencies = [
"generic-array",
]
[[package]]
name = "block-padding"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93"
dependencies = [
"generic-array",
]
[[package]]
name = "bstr"
version = "1.11.3"
@ -1180,9 +1213,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
dependencies = [
"generic-array",
"rand_core",
"typenum",
]
[[package]]
name = "ctr"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835"
dependencies = [
"cipher",
]
[[package]]
name = "ctrlc"
version = "3.4.5"
@ -1260,6 +1303,7 @@ dependencies = [
"block-buffer",
"const-oid",
"crypto-common",
"subtle",
]
[[package]]
@ -1336,6 +1380,7 @@ dependencies = [
"hyper-util",
"log",
"mlua",
"ncr",
"tokio",
]
@ -1539,6 +1584,16 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "ghash"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1"
dependencies = [
"opaque-debug",
"polyval",
]
[[package]]
name = "gimli"
version = "0.31.1"
@ -1655,6 +1710,15 @@ dependencies = [
"tracing",
]
[[package]]
name = "hmac"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
dependencies = [
"digest",
]
[[package]]
name = "http"
version = "1.2.0"
@ -1943,6 +2007,7 @@ version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01"
dependencies = [
"block-padding",
"generic-array",
]
@ -2146,6 +2211,24 @@ dependencies = [
"pkg-config",
]
[[package]]
name = "ncr"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e905418cb5217c1e6947e8d90e0cfd7468352dbe4e6d34a0a6e7ed51e8edfcf2"
dependencies = [
"aes",
"aes-gcm",
"base64 0.21.7",
"cfb8",
"cipher",
"hmac",
"pbkdf2",
"phf",
"rand",
"sha1",
]
[[package]]
name = "nix"
version = "0.29.0"
@ -2306,6 +2389,12 @@ version = "1.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e"
[[package]]
name = "opaque-debug"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381"
[[package]]
name = "overload"
version = "0.1.1"
@ -2341,6 +2430,15 @@ dependencies = [
"windows-targets",
]
[[package]]
name = "pbkdf2"
version = "0.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2"
dependencies = [
"digest",
]
[[package]]
name = "pem-rfc7468"
version = "0.7.0"
@ -2366,6 +2464,24 @@ dependencies = [
"indexmap 2.7.1",
]
[[package]]
name = "phf"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078"
dependencies = [
"phf_shared",
]
[[package]]
name = "phf_shared"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5"
dependencies = [
"siphasher",
]
[[package]]
name = "pin-project"
version = "1.1.9"
@ -2425,6 +2541,18 @@ version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2"
[[package]]
name = "polyval"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25"
dependencies = [
"cfg-if",
"cpufeatures",
"opaque-debug",
"universal-hash",
]
[[package]]
name = "ppv-lite86"
version = "0.2.20"
@ -2867,6 +2995,17 @@ dependencies = [
"digest",
]
[[package]]
name = "sha1"
version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
]
[[package]]
name = "sha2"
version = "0.10.8"
@ -2955,6 +3094,12 @@ dependencies = [
"thiserror 1.0.69",
]
[[package]]
name = "siphasher"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d"
[[package]]
name = "slab"
version = "0.4.9"
@ -3432,6 +3577,16 @@ version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
[[package]]
name = "universal-hash"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea"
dependencies = [
"crypto-common",
"subtle",
]
[[package]]
name = "untrusted"
version = "0.9.0"

View File

@ -28,6 +28,7 @@ hyper = { version = "1", features = ["server"] }
hyper-util = "0"
log = { version = "0" }
mlua = { version = "0", features = ["async", "luajit", "send"] }
ncr = { version = "0", features = ["cfb8", "ecb", "gcm"] }
tokio = { version = "1", features = ["macros"] }
[features]

View File

@ -11,6 +11,7 @@ A Minecraft bot with Lua scripting support, written in Rust with [azalea](https:
- Listening to in-game events
- Pathfinding (from azalea)
- Entity and chest interaction
- NoChatReports encryption
## Usage

View File

@ -4,6 +4,8 @@ use crate::{
};
use azalea::{brigadier::prelude::*, chat::ChatPacket, prelude::*};
use futures::lock::Mutex;
use mlua::{Function, Table};
use ncr::utils::prepend_header;
pub type Ctx = CommandContext<Mutex<CommandSource>>;
@ -11,16 +13,24 @@ pub struct CommandSource {
pub client: Client,
pub message: ChatPacket,
pub state: State,
pub ncr_options: Option<Table>,
}
impl CommandSource {
pub fn reply(&self, message: &str) {
for chunk in message
for mut chunk in message
.chars()
.collect::<Vec<char>>()
.chunks(236)
.chunks(if self.ncr_options.is_some() { 150 } else { 236 })
.map(|chars| chars.iter().collect::<String>())
{
if let (Some(options), Ok(encrypt)) = (
&self.ncr_options,
self.state.lua.globals().get::<Function>("ncr_encrypt"),
) && let Ok(encrypted) = encrypt.call::<String>((options, prepend_header(&chunk)))
{
chunk = encrypted
}
self.client.chat(
&(if self.message.is_whisper()
&& let Some(username) = self.message.username()

View File

@ -9,7 +9,8 @@ use azalea::{prelude::*, protocol::packets::game::ClientboundGamePacket};
use hyper::{server::conn::http1, service::service_fn};
use hyper_util::rt::TokioIo;
use log::{debug, error, info, trace};
use mlua::IntoLuaMulti;
use mlua::{Function, IntoLuaMulti, Table};
use ncr::utils::trim_header;
use std::process::exit;
use tokio::net::TcpListener;
@ -20,32 +21,44 @@ pub async fn handle_event(client: Client, event: Event, state: State) -> anyhow:
call_listeners(&state, "add_player", Player::from(player_info)).await;
}
Event::Chat(message) => {
let globals = state.lua.globals();
let (sender, mut content) = message.split_sender_and_content();
let formatted_message = message.message();
info!("{}", formatted_message.to_ansi());
if message.is_whisper()
&& let (Some(sender), content) = message.split_sender_and_content()
&& state
.lua
.globals()
.get::<Vec<String>>("Owners")?
.contains(&sender)
{
if let Err(error) = state.commands.execute(
content,
CommandSource {
client: client.clone(),
message: message.clone(),
state: state.clone(),
if let Some(sender) = sender {
let mut ncr_options = None;
if let (Ok(options), Ok(decrypt)) = (
globals.get::<Table>("NcrOptions"),
globals.get::<Function>("ncr_decrypt"),
) && let Ok(decrypted) =
decrypt.call::<String>((options.clone(), content.clone()))
&& let Ok(trimmed) = trim_header(&decrypted)
{
ncr_options = Some(options);
content = trimmed.to_owned();
info!("Decrypted message from {sender}: {content}");
}
if message.is_whisper() && globals.get::<Vec<String>>("Owners")?.contains(&sender) {
if let Err(error) = state.commands.execute(
content,
CommandSource {
client: client.clone(),
message: message.clone(),
state: state.clone(),
ncr_options: ncr_options.clone(),
}
.into(),
) {
CommandSource {
client,
message,
state: state.clone(),
ncr_options,
}
.reply(&format!("{error:?}"));
}
.into(),
) {
CommandSource {
client,
message,
state: state.clone(),
}
.reply(&format!("{error:?}"));
}
}

View File

@ -4,6 +4,7 @@ pub mod container;
pub mod direction;
pub mod events;
pub mod logging;
pub mod nochatreports;
pub mod player;
pub mod system;
pub mod vec3;
@ -39,6 +40,7 @@ pub fn register_functions(
block::register_functions(lua, globals)?;
events::register_functions(lua, globals, event_listeners)?;
logging::register_functions(lua, globals)?;
nochatreports::register_functions(lua, globals)?;
system::register_functions(lua, globals)
}

View File

@ -0,0 +1,41 @@
macro_rules! crypt_with {
($op:ident, $encoding:expr, $key:expr, $text:expr, $algo:ident) => {
match $encoding {
1 => $algo::<Base64Encoding>::$op($text, $key),
2 => $algo::<Base64rEncoding>::$op($text, $key),
_ => $algo::<NewBase64rEncoding>::$op($text, $key),
}
.map_err(|error| Error::external(error.to_string()))?
};
}
#[macro_export]
macro_rules! crypt {
($op:ident, $encoding:expr, $options:expr, $text:expr) => {
match $options.get("encryption").unwrap_or_default() {
1 => CaesarEncryption::$op(&$text, &$options.get("key")?)
.map_err(|error| Error::external(error.to_string()))?,
2 => crypt_with!(
$op,
$encoding,
&$options.get::<UserDataRef<AesKey>>("key")?.inner,
&$text,
EcbEncryption
),
3 => crypt_with!(
$op,
$encoding,
&$options.get::<UserDataRef<AesKey>>("key")?.inner,
&$text,
GcmEncryption
),
_ => crypt_with!(
$op,
$encoding,
&$options.get::<UserDataRef<AesKey>>("key")?.inner,
&$text,
Cfb8Encryption
),
}
};
}

View File

@ -0,0 +1,12 @@
use mlua::UserData;
pub struct AesKey {
pub inner: ncr::AesKey,
}
impl UserData for AesKey {
fn add_fields<F: mlua::UserDataFields<Self>>(f: &mut F) {
f.add_field_method_get("base64", |_, this| Ok(this.inner.encode_base64()));
f.add_field_method_get("bytes", |_, this| Ok(this.inner.as_ref().to_vec()));
}
}

View File

@ -0,0 +1,81 @@
#[macro_use]
pub mod crypt;
pub mod key;
use key::AesKey;
use mlua::{Error, Lua, Result, Table, UserDataRef};
use ncr::{
encoding::{Base64Encoding, Base64rEncoding, NewBase64rEncoding},
encryption::{CaesarEncryption, Cfb8Encryption, EcbEncryption, Encryption, GcmEncryption},
utils::{prepend_header, trim_header},
};
pub fn register_functions(lua: &Lua, globals: &Table) -> Result<()> {
globals.set(
"ncr_aes_key_from_passphrase",
lua.create_function(|_, passphrase: Vec<u8>| {
Ok(AesKey {
inner: ncr::AesKey::gen_from_passphrase(&passphrase),
})
})?,
)?;
globals.set(
"ncr_aes_key_from_base64",
lua.create_function(|_, base64: String| {
Ok(AesKey {
inner: ncr::AesKey::decode_base64(&base64)
.map_err(|error| Error::external(error.to_string()))?,
})
})?,
)?;
globals.set(
"ncr_generate_random_aes_key",
lua.create_function(|_, (): ()| {
Ok(AesKey {
inner: ncr::AesKey::gen_random_key(),
})
})?,
)?;
globals.set(
"ncr_encrypt",
lua.create_function(|_, (options, plaintext): (Table, String)| {
Ok(crypt!(
encrypt,
options.get("encoding").unwrap_or_default(),
options,
plaintext
))
})?,
)?;
globals.set(
"ncr_decrypt",
lua.create_function(|_, (options, ciphertext): (Table, String)| {
Ok(crypt!(
decrypt,
options.get("encoding").unwrap_or_default(),
options,
ciphertext
))
})?,
)?;
globals.set(
"ncr_prepend_header",
lua.create_function(|_, text: String| Ok(prepend_header(&text)))?,
)?;
globals.set(
"ncr_trim_header",
lua.create_function(|_, text: String| {
Ok(trim_header(&text)
.map_err(|error| Error::external(error.to_string()))?
.to_owned())
})?,
)?;
Ok(())
}