use bytes::BufMut;
use hmac::{Hmac, Mac};
use sha2::Sha256;
use deepslate_protocol::types::{self, GameProfile};
use deepslate_protocol::varint;
pub const VELOCITY_FORWARDING_CHANNEL: &str = "velocity:player_info";
pub mod version {
pub const MODERN_DEFAULT: i32 = 1;
pub const MODERN_WITH_KEY: i32 = 2;
pub const MODERN_WITH_KEY_V2: i32 = 3;
pub const MODERN_LAZY_SESSION: i32 = 4;
pub const MODERN_MAX: i32 = MODERN_LAZY_SESSION;
}
type HmacSha256 = Hmac<Sha256>;
#[must_use]
pub fn create_forwarding_data(
secret: &[u8],
player_address: &str,
profile: &GameProfile,
requested_version: i32,
) -> Vec<u8> {
let actual_version = if requested_version >= version::MODERN_LAZY_SESSION {
version::MODERN_LAZY_SESSION
} else {
version::MODERN_DEFAULT
};
let mut data = Vec::with_capacity(256);
varint::write_var_int(&mut data, actual_version);
types::write_string(&mut data, player_address);
types::write_uuid(&mut data, profile.id);
types::write_string(&mut data, &profile.name);
types::write_properties(&mut data, &profile.properties);
let mut mac = HmacSha256::new_from_slice(secret).expect("HMAC can take key of any size");
mac.update(&data);
let signature = mac.finalize().into_bytes();
let mut result = Vec::with_capacity(32 + data.len());
result.put_slice(&signature);
result.extend_from_slice(&data);
result
}
#[cfg(test)]
mod tests {
use uuid::Uuid;
use deepslate_protocol::types::ProfileProperty;
use super::*;
#[test]
fn test_create_forwarding_data_structure() {
let secret = b"test-secret";
let profile = GameProfile {
id: Uuid::parse_str("069a79f4-44e9-4726-a5be-fca90e38aaf5").unwrap(),
name: "Notch".to_string(),
properties: vec![ProfileProperty {
name: "textures".to_string(),
value: "base64data".to_string(),
signature: None,
}],
};
let data = create_forwarding_data(secret, "127.0.0.1", &profile, version::MODERN_MAX);
assert!(data.len() > 32);
let forwarding_data = &data[32..];
let mut cursor = forwarding_data;
let ver = deepslate_protocol::varint::read_var_int(&mut cursor).unwrap();
assert_eq!(ver, version::MODERN_LAZY_SESSION);
}
#[test]
fn test_hmac_is_deterministic() {
let secret = b"my-secret";
let profile = GameProfile {
id: Uuid::nil(),
name: "Steve".to_string(),
properties: vec![],
};
let d1 = create_forwarding_data(secret, "10.0.0.1", &profile, version::MODERN_MAX);
let d2 = create_forwarding_data(secret, "10.0.0.1", &profile, version::MODERN_MAX);
assert_eq!(d1, d2);
}
#[test]
fn test_different_secret_produces_different_hmac() {
let profile = GameProfile {
id: Uuid::nil(),
name: "Steve".to_string(),
properties: vec![],
};
let d1 = create_forwarding_data(b"secret-a", "10.0.0.1", &profile, version::MODERN_MAX);
let d2 = create_forwarding_data(b"secret-b", "10.0.0.1", &profile, version::MODERN_MAX);
assert_ne!(d1[..32], d2[..32]); assert_eq!(d1[32..], d2[32..]); }
}