Skip to content

Commit

Permalink
Minor changes
Browse files Browse the repository at this point in the history
More to come
  • Loading branch information
guac42 committed May 4, 2023
1 parent 39cf110 commit 7e46176
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 72 deletions.
16 changes: 5 additions & 11 deletions crates/valence/examples/chat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
use tracing::warn;
use valence::client::despawn_disconnected_clients;
use valence::client::misc::CommandExecution;
use valence::entity::player::PlayerEntityBundle;
use valence::prelude::*;

const SPAWN_Y: i32 = 64;
Expand Down Expand Up @@ -46,20 +45,15 @@ fn setup(
}

fn init_clients(
mut clients: Query<(Entity, &UniqueId, &mut Client, &mut GameMode), Added<Client>>,
mut clients: Query<(&mut Client, &mut Location, &mut Position, &mut GameMode), Added<Client>>,
instances: Query<Entity, With<Instance>>,
mut commands: Commands,
) {
for (entity, uuid, mut client, mut game_mode) in &mut clients {
for (mut client, mut loc, mut pos, mut game_mode) in &mut clients {
*game_mode = GameMode::Creative;
client.send_message("Welcome to Valence! Say something.".italic());
loc.0 = instances.single();
pos.set([0.0, SPAWN_Y as f64 + 1.0, 0.0]);

commands.entity(entity).insert(PlayerEntityBundle {
location: Location(instances.single()),
position: Position::new([0.0, SPAWN_Y as f64 + 1.0, 0.0]),
uuid: *uuid,
..Default::default()
});
client.send_message("Welcome to Valence! Say something.".italic());
}
}

Expand Down
8 changes: 4 additions & 4 deletions crates/valence/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ mod tests;
#[cfg(feature = "anvil")]
pub use valence_anvil as anvil;
#[cfg(feature = "chat")]
pub use valence_chat as secure_chat;
pub use valence_chat as chat;
pub use valence_core::*;
#[cfg(feature = "inventory")]
pub use valence_inventory as inventory;
Expand Down Expand Up @@ -61,6 +61,8 @@ pub mod prelude {
pub use biome::{Biome, BiomeId, BiomeRegistry};
pub use block::{BlockKind, BlockState, PropName, PropValue};
pub use block_pos::BlockPos;
#[cfg(feature = "chat")]
pub use chat::chat_type::{ChatType, ChatTypeRegistry};
pub use chunk_pos::{ChunkPos, ChunkView};
pub use client::action::*;
pub use client::command::*;
Expand Down Expand Up @@ -98,8 +100,6 @@ pub mod prelude {
pub use packet::s2c::play::particle::Particle;
#[cfg(feature = "player_list")]
pub use player_list::{PlayerList, PlayerListEntry};
#[cfg(feature = "chat")]
pub use secure_chat::chat_type::{ChatType, ChatTypeRegistry};
pub use text::{Color, Text, TextFormat};
#[cfg(feature = "advancement")]
pub use valence_advancement::{
Expand Down Expand Up @@ -163,7 +163,7 @@ impl PluginGroup for DefaultPlugins {

#[cfg(feature = "chat")]
{
group = group.add(valence_chat::SecureChatPlugin);
group = group.add(valence_chat::ChatPlugin);
}

group
Expand Down
6 changes: 2 additions & 4 deletions crates/valence_chat/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,5 @@ Provides support for cryptographically verified chat messaging on the server.

This crate contains the secure chat plugin as well as chat types and the chat type registry. Minecraft's default chat types are added to the registry by default. Chat types contain information about how chat is styled, such as the chat color.

### **NOTE:**
- Modifying the chat type registry after the server has started can
break invariants within instances and clients! Make sure there are no
instances or clients spawned before mutating.

This crate also contains the `yggdrasil_session_pubkey.der` file which is an encoded format of Mojang's public key. This is necessary to verify the integrity of our clients' public session key, which is used for validating chat messages. In reality Mojang's key should never change in order to maintain backwards compatibility with older versions, but if it does it can be extracted from any minecraft server jar.
99 changes: 46 additions & 53 deletions crates/valence_chat/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ pub mod chat_type;
use std::collections::VecDeque;
use std::time::SystemTime;

use anyhow::bail;
use bevy_app::prelude::*;
use bevy_ecs::prelude::*;
use chat_type::ChatTypePlugin;
Expand All @@ -30,7 +31,7 @@ use rsa::{PaddingScheme, PublicKey, RsaPublicKey};
use rustc_hash::{FxHashMap, FxHashSet};
use sha1::{Digest, Sha1};
use sha2::Sha256;
use tracing::{info, trace, warn};
use tracing::{info, warn};
use uuid::Uuid;
use valence_client::misc::{ChatMessage, MessageAcknowledgment, PlayerSession};
use valence_client::settings::ClientSettings;
Expand All @@ -51,7 +52,33 @@ use valence_core::translation_key::{
use valence_core::uuid::UniqueId;
use valence_player_list::{ChatSession, PlayerListEntry};

const MOJANG_KEY_DATA: &[u8] = include_bytes!("../../../assets/yggdrasil_session_pubkey.der");
const MOJANG_KEY_DATA: &[u8] = include_bytes!("../yggdrasil_session_pubkey.der");

pub struct ChatPlugin;

impl Plugin for ChatPlugin {
fn build(&self, app: &mut bevy_app::App) {
let mojang_pub_key = RsaPublicKey::from_public_key_der(MOJANG_KEY_DATA)
.expect("Error creating Mojang public key");

app.add_plugin(ChatTypePlugin)
.insert_resource(MojangServicesState::new(mojang_pub_key))
.add_systems(
(
init_chat_states,
handle_session_events
.after(init_chat_states)
.before(handle_message_events),
handle_message_acknowledgement
.after(init_chat_states)
.before(handle_message_events),
handle_message_events.after(init_chat_states),
)
.in_base_set(CoreSet::PostUpdate)
.before(FlushPacketsSet),
);
}
}

#[derive(Resource)]
struct MojangServicesState {
Expand Down Expand Up @@ -149,10 +176,9 @@ impl AcknowledgementValidator {
&mut self,
acknowledgements: &[u8; 3],
message_index: i32,
) -> Option<VecDeque<[u8; 256]>> {
) -> anyhow::Result<VecDeque<[u8; 256]>> {
if !self.remove_until(message_index) {
// Invalid message index
return None;
bail!("Invalid message index");
}

let acknowledged_count = {
Expand All @@ -164,15 +190,13 @@ impl AcknowledgementValidator {
};

if acknowledged_count > 20 {
// Too many message acknowledgements, protocol error?
return None;
bail!("Too many message acknowledgements, protocol error?");
}

let mut list = VecDeque::with_capacity(acknowledged_count);
for i in 0..20 {
let acknowledgement = acknowledgements[i >> 3] & (0b1 << (i % 8)) != 0;
// SAFETY: The length of messages is never less than 20
let acknowledged_message = unsafe { self.messages.get_unchecked_mut(i) };
let acknowledged_message = &mut self.messages[i];
// Client has acknowledged the i-th message
if acknowledgement {
// The validator has the i-th message
Expand All @@ -181,27 +205,23 @@ impl AcknowledgementValidator {
list.push_back(m.signature);
} else {
// Client has acknowledged a non-existing message
trace!("Client has acknowledged a non-existing message");
return None;
bail!("Client has acknowledged a non-existing message");
}
} else {
// Client has not acknowledged the i-th message
if matches!(acknowledged_message, Some(m) if !m.pending) {
// The validator has an i-th message that has been validated but the client
// claims that it hasn't been validated yet
trace!(
bail!(
"The validator has an i-th message that has been validated but the client \
claims that it hasn't been validated yet"
);
return None;
}
// Honestly not entirely sure why this is done
if acknowledged_message.is_some() {
*acknowledged_message = None;
}
*acknowledged_message = None;
}
}
Some(list)
Ok(list)
}

/// The number of pending messages in the validator.
Expand Down Expand Up @@ -310,32 +330,6 @@ impl MessageSignatureStorage {
}
}

pub struct SecureChatPlugin;

impl Plugin for SecureChatPlugin {
fn build(&self, app: &mut bevy_app::App) {
let mojang_pub_key = RsaPublicKey::from_public_key_der(MOJANG_KEY_DATA)
.expect("Error creating Mojang public key");

app.add_plugin(ChatTypePlugin)
.insert_resource(MojangServicesState::new(mojang_pub_key))
.add_systems(
(
init_chat_states,
handle_session_events
.after(init_chat_states)
.before(handle_message_events),
handle_message_acknowledgement
.after(init_chat_states)
.before(handle_message_events),
handle_message_events.after(init_chat_states),
)
.in_base_set(CoreSet::PostUpdate)
.before(FlushPacketsSet),
);
}
}

fn init_chat_states(clients: Query<Entity, Added<Client>>, mut commands: Commands) {
for entity in clients.iter() {
commands.entity(entity).insert(ChatState::default());
Expand Down Expand Up @@ -369,15 +363,11 @@ fn handle_session_events(
continue;
}

// Serialize the session data.
let mut serialized = Vec::with_capacity(318);
serialized.extend_from_slice(uuid.0.into_bytes().as_slice());
serialized.extend_from_slice(session.session_data.expires_at.to_be_bytes().as_ref());
serialized.extend_from_slice(session.session_data.public_key_data.as_ref());

// Hash the session data using the SHA-1 algorithm.
let mut hasher = Sha1::new();
hasher.update(&serialized);
hasher.update(uuid.0.into_bytes());
hasher.update(session.session_data.expires_at.to_be_bytes());
hasher.update(&session.session_data.public_key_data);
let hash = hasher.finalize();

// Verify the session data using Mojang's public key and the hashed session data
Expand Down Expand Up @@ -507,15 +497,18 @@ fn handle_message_events(
.validator
.validate(&message.acknowledgements, message.message_index)
{
None => {
warn!("Failed to validate acknowledgements from `{}`", username.0);
Err(error) => {
warn!(
"Failed to validate acknowledgements from `{}`: {}",
username.0, error
);
commands.add(DisconnectClient {
client: message.client,
reason: Text::translate(MULTIPLAYER_DISCONNECT_CHAT_VALIDATION_FAILED, []),
});
continue;
}
Some(last_seen) => last_seen,
Ok(last_seen) => last_seen,
};

let Some(link) = &state.chain.next_link() else {
Expand Down
File renamed without changes.

0 comments on commit 7e46176

Please sign in to comment.