aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormat <27899617+mat-1@users.noreply.github.com>2022-04-25 03:50:17 +0000
committerGitHub <noreply@github.com>2022-04-25 03:50:17 +0000
commitdac943e3d79def7d2c322450790f16f027735941 (patch)
tree3beafa3c8035c69a33b7d0f12779236bf786cf36
parentb7641ff308aab7840d2a2253ae50f8ee496b2a97 (diff)
parentf4dd3a9293367fa8f3d839a184e8055b22595204 (diff)
downloadazalea-drasl-dac943e3d79def7d2c322450790f16f027735941.tar.xz
Merge pull request #3 from mat-1/auth
Auth
-rwxr-xr-xCargo.lock215
-rwxr-xr-xazalea-auth/Cargo.toml6
-rw-r--r--azalea-auth/src/encryption.rs94
-rwxr-xr-xazalea-auth/src/lib.rs3
-rwxr-xr-xazalea-client/Cargo.toml1
-rwxr-xr-xazalea-client/src/connect.rs20
-rwxr-xr-xazalea-protocol/src/connect.rs52
-rwxr-xr-xazalea-protocol/src/mc_buf/mod.rs20
-rwxr-xr-xazalea-protocol/src/mc_buf/read.rs18
-rwxr-xr-xazalea-protocol/src/mc_buf/write.rs10
-rwxr-xr-xazalea-protocol/src/packets/login/clientbound_hello_packet.rs7
-rwxr-xr-xazalea-protocol/src/packets/login/mod.rs8
-rwxr-xr-xazalea-protocol/src/packets/login/serverbound_hello_packet.rs24
-rw-r--r--azalea-protocol/src/packets/login/serverbound_key_packet.rs10
-rwxr-xr-xazalea-protocol/src/read.rs62
-rwxr-xr-xazalea-protocol/src/write.rs13
-rw-r--r--[-rwxr-xr-x]bot/src/main.rs0
17 files changed, 513 insertions, 50 deletions
diff --git a/Cargo.lock b/Cargo.lock
index a0d60170..8be7aa31 100755
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -9,6 +9,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
+name = "aes"
+version = "0.7.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8"
+dependencies = [
+ "cfg-if",
+ "cipher",
+ "cpufeatures",
+ "opaque-debug",
+]
+
+[[package]]
name = "async-compression"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -64,6 +76,12 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
name = "azalea-auth"
version = "0.1.0"
dependencies = [
+ "aes",
+ "cfb8",
+ "num-bigint",
+ "rand",
+ "rsa_public_encrypt_pkcs1",
+ "sha-1",
"uuid",
]
@@ -84,6 +102,7 @@ dependencies = [
name = "azalea-client"
version = "0.1.0"
dependencies = [
+ "azalea-auth",
"azalea-protocol",
]
@@ -142,6 +161,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
+name = "block-buffer"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1d36a02058e76b040de25a4464ba1c80935655595b661505c8b39b664828b95"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
name = "bot"
version = "0.1.0"
dependencies = [
@@ -190,12 +218,40 @@ dependencies = [
]
[[package]]
+name = "cfb8"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3a4b6c43bf284e617a659ce5dc149676680530a3a4a9bb6b278d1a9ed5b229d"
+dependencies = [
+ "cipher",
+]
+
+[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
+name = "chrono"
+version = "0.4.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
+dependencies = [
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
+name = "cipher"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
name = "clap"
version = "2.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -207,6 +263,15 @@ dependencies = [
]
[[package]]
+name = "cpufeatures"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469"
+dependencies = [
+ "libc",
+]
+
+[[package]]
name = "crc32fast"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -298,6 +363,16 @@ dependencies = [
]
[[package]]
+name = "crypto-common"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57952ca27b5e3606ff4dd79b0020231aaf9d6aa76dc05fd30137538c50bd3ce8"
+dependencies = [
+ "generic-array",
+ "typenum",
+]
+
+[[package]]
name = "csv"
version = "1.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -326,6 +401,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57"
[[package]]
+name = "digest"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b697d66081d42af4fba142d56918a3cb21dc8eb63372c6b85d14f44fb9c5979b"
+dependencies = [
+ "block-buffer",
+ "crypto-common",
+ "generic-array",
+]
+
+[[package]]
name = "either"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -428,6 +514,16 @@ dependencies = [
]
[[package]]
+name = "generic-array"
+version = "0.14.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817"
+dependencies = [
+ "typenum",
+ "version_check",
+]
+
+[[package]]
name = "getrandom"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -649,6 +745,40 @@ dependencies = [
]
[[package]]
+name = "num"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "43db66d1170d347f9a065114077f7dccb00c1b9478c89384490a3425279a4606"
+dependencies = [
+ "num-bigint",
+ "num-complex",
+ "num-integer",
+ "num-iter",
+ "num-rational",
+ "num-traits",
+]
+
+[[package]]
+name = "num-bigint"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f"
+dependencies = [
+ "autocfg",
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
+name = "num-complex"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26873667bbbb7c5182d4a37c1add32cdf09f841af72da53318fdb81543c15085"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
name = "num-derive"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -660,6 +790,39 @@ dependencies = [
]
[[package]]
+name = "num-integer"
+version = "0.1.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
+dependencies = [
+ "autocfg",
+ "num-traits",
+]
+
+[[package]]
+name = "num-iter"
+version = "0.1.42"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2021c8337a54d21aca0d59a92577a029af9431cb59b909b03252b9c164fad59"
+dependencies = [
+ "autocfg",
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
+name = "num-rational"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d41702bd167c2df5520b384281bc111a4b5efcf7fbc4c9c222c815b07e0a6a6a"
+dependencies = [
+ "autocfg",
+ "num-bigint",
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
name = "num-traits"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -685,6 +848,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
[[package]]
+name = "opaque-debug"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
+
+[[package]]
name = "packet-macros"
version = "0.1.0"
dependencies = [
@@ -900,6 +1069,17 @@ dependencies = [
]
[[package]]
+name = "rsa_public_encrypt_pkcs1"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b3e9243a1f8b312c5535c09de102cc061416515201b194ee4f0a9a76da20ebf4"
+dependencies = [
+ "num",
+ "rand",
+ "simple_asn1",
+]
+
+[[package]]
name = "rustc_version"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -977,6 +1157,29 @@ dependencies = [
]
[[package]]
+name = "sha-1"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "digest",
+]
+
+[[package]]
+name = "simple_asn1"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8eb4ea60fb301dc81dfc113df680571045d375ab7345d171c5dc7d7e13107a80"
+dependencies = [
+ "chrono",
+ "num-bigint",
+ "num-traits",
+ "thiserror",
+]
+
+[[package]]
name = "slab"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1151,6 +1354,12 @@ dependencies = [
]
[[package]]
+name = "typenum"
+version = "1.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
+
+[[package]]
name = "unicode-bidi"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1202,6 +1411,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
[[package]]
+name = "version_check"
+version = "0.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
+
+[[package]]
name = "walkdir"
version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/azalea-auth/Cargo.toml b/azalea-auth/Cargo.toml
index aa9b7bdb..6279d903 100755
--- a/azalea-auth/Cargo.toml
+++ b/azalea-auth/Cargo.toml
@@ -6,4 +6,10 @@ version = "0.1.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
+aes = "0.7.4"
+cfb8 = "0.7.1"
+num-bigint = "^0.4.3"
+rand = {version = "^0.8.4", features = ["getrandom"]}
+rsa_public_encrypt_pkcs1 = "0.4.0"
+sha-1 = "^0.10.0"
uuid = "^0.8.2"
diff --git a/azalea-auth/src/encryption.rs b/azalea-auth/src/encryption.rs
new file mode 100644
index 00000000..6f2e43a8
--- /dev/null
+++ b/azalea-auth/src/encryption.rs
@@ -0,0 +1,94 @@
+use aes::{
+ cipher::{AsyncStreamCipher, NewCipher},
+ Aes128,
+};
+use cfb8::Cfb8;
+use rand::{rngs::OsRng, RngCore};
+use sha1::{Digest, Sha1};
+
+fn generate_secret_key() -> [u8; 16] {
+ let mut key = [0u8; 16];
+ OsRng.fill_bytes(&mut key);
+ key
+}
+
+fn digest_data(server_id: &[u8], public_key: &[u8], private_key: &[u8]) -> Vec<u8> {
+ let mut digest = Sha1::new();
+ digest.update(server_id);
+ digest.update(public_key);
+ digest.update(private_key);
+ digest.finalize().to_vec()
+}
+
+fn hex_digest(digest: &[u8]) -> String {
+ // Note that the Sha1.hexdigest() method used by minecraft is non standard.
+ // It doesn't match the digest method found in most programming languages
+ // and libraries. It works by treating the sha1 output bytes as one large
+ // integer in two's complement and then printing the integer in base 16,
+ // placing a minus sign if the interpreted number is negative.
+ num_bigint::BigInt::from_signed_bytes_be(digest).to_str_radix(16)
+}
+
+#[derive(Debug)]
+pub struct EncryptResult {
+ pub secret_key: [u8; 16],
+ pub encrypted_public_key: Vec<u8>,
+ pub encrypted_nonce: Vec<u8>,
+}
+
+pub fn encrypt(public_key: &[u8], nonce: &[u8]) -> Result<EncryptResult, String> {
+ // On receipt of a Encryption Request from the server, the client will
+ // generate a random 16-byte shared secret, to be used with the AES/CFB8
+ // stream cipher.
+ let secret_key = generate_secret_key();
+ // let hash = hex_digest(&digest_data(server_id.as_bytes(), public_key, &secret_key));
+
+ // this.keybytes = Crypt.encryptUsingKey(publicKey, secretKey.getEncoded());
+ // this.nonce = Crypt.encryptUsingKey(publicKey, arrby);
+ let encrypted_public_key: Vec<u8> =
+ rsa_public_encrypt_pkcs1::encrypt(&public_key, &secret_key)?;
+ let encrypted_nonce: Vec<u8> = rsa_public_encrypt_pkcs1::encrypt(&public_key, &nonce)?;
+
+ Ok(EncryptResult {
+ secret_key,
+ encrypted_public_key,
+ encrypted_nonce,
+ })
+}
+
+// TODO: update the aes and cfb8 crates
+pub type Aes128Cfb = Cfb8<Aes128>;
+
+pub fn create_cipher(key: &[u8]) -> Aes128Cfb {
+ Aes128Cfb::new_from_slices(&key, &key).unwrap()
+}
+
+pub fn encrypt_packet(cipher: &mut Aes128Cfb, packet: &mut [u8]) {
+ cipher.encrypt(packet);
+}
+pub fn decrypt_packet(cipher: &mut Aes128Cfb, packet: &mut [u8]) {
+ cipher.decrypt(packet);
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_generate_secret_key() {
+ let key = generate_secret_key();
+ assert_eq!(key.len(), 16);
+ }
+
+ #[test]
+ fn test_hex_digest() {
+ let digest = hex_digest(&digest_data(b"Notch", &[], &[]));
+ assert_eq!(digest, "4ed1f46bbe04bc756bcb17c0c7ce3e4632f06a48");
+
+ let digest = hex_digest(&digest_data(b"jeb_", &[], &[]));
+ assert_eq!(digest, "-7c9d5b0044c130109a5d7b5fb5c317c02b4e28c1");
+
+ let digest = hex_digest(&digest_data(b"simon", &[], &[]));
+ assert_eq!(digest, "88e16a1019277b15d58faf0541e11910eb756f6");
+ }
+}
diff --git a/azalea-auth/src/lib.rs b/azalea-auth/src/lib.rs
index 773ea1d9..afe9eb33 100755
--- a/azalea-auth/src/lib.rs
+++ b/azalea-auth/src/lib.rs
@@ -1,3 +1,6 @@
//! Handle Minecraft authentication.
pub mod game_profile;
+pub mod encryption;
+
+
diff --git a/azalea-client/Cargo.toml b/azalea-client/Cargo.toml
index 7e22f727..5769f289 100755
--- a/azalea-client/Cargo.toml
+++ b/azalea-client/Cargo.toml
@@ -6,4 +6,5 @@ version = "0.1.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
+azalea-auth = {path = "../azalea-auth"}
azalea-protocol = {path = "../azalea-protocol"}
diff --git a/azalea-client/src/connect.rs b/azalea-client/src/connect.rs
index 6e58bee6..44eff33f 100755
--- a/azalea-client/src/connect.rs
+++ b/azalea-client/src/connect.rs
@@ -4,7 +4,10 @@ use azalea_protocol::{
packets::{
game::GamePacket,
handshake::client_intention_packet::ClientIntentionPacket,
- login::{serverbound_hello_packet::ServerboundHelloPacket, LoginPacket},
+ login::{
+ serverbound_hello_packet::ServerboundHelloPacket,
+ serverbound_key_packet::ServerboundKeyPacket, LoginPacket,
+ },
ConnectionProtocol, PROTOCOL_VERSION,
},
resolver, ServerAddress,
@@ -39,6 +42,20 @@ pub async fn join_server(address: &ServerAddress) -> Result<(), String> {
Ok(packet) => match packet {
LoginPacket::ClientboundHelloPacket(p) => {
println!("Got encryption request {:?} {:?}", p.nonce, p.public_key);
+ let e = azalea_auth::encryption::encrypt(&p.public_key, &p.nonce).unwrap();
+
+ // TODO: authenticate with the server here (authenticateServer)
+ println!("Sending encryption response {:?}", e);
+
+ conn.write(
+ ServerboundKeyPacket {
+ nonce: e.encrypted_nonce.into(),
+ shared_secret: e.encrypted_public_key.into(),
+ }
+ .get(),
+ )
+ .await;
+ conn.set_encryption_key(e.secret_key);
}
LoginPacket::ClientboundLoginCompressionPacket(p) => {
println!("Got compression request {:?}", p.compression_threshold);
@@ -57,6 +74,7 @@ pub async fn join_server(address: &ServerAddress) -> Result<(), String> {
LoginPacket::ClientboundCustomQueryPacket(p) => {
println!("Got custom query {:?}", p);
}
+ LoginPacket::ServerboundKeyPacket(_) => todo!(),
},
Err(e) => {
panic!("Error: {:?}", e);
diff --git a/azalea-protocol/src/connect.rs b/azalea-protocol/src/connect.rs
index 3d910d3a..e81bc368 100755
--- a/azalea-protocol/src/connect.rs
+++ b/azalea-protocol/src/connect.rs
@@ -7,6 +7,7 @@ use crate::packets::status::StatusPacket;
use crate::read::read_packet;
use crate::write::write_packet;
use crate::ServerIpAddress;
+use azalea_auth::encryption::Aes128Cfb;
use tokio::net::TcpStream;
pub enum PacketFlow {
@@ -25,6 +26,7 @@ pub struct GameConnection {
/// The buffered writer
pub stream: TcpStream,
pub compression_threshold: Option<u32>,
+ pub cipher: Option<Aes128Cfb>,
}
pub struct StatusConnection {
@@ -38,6 +40,7 @@ pub struct LoginConnection {
/// The buffered writer
pub stream: TcpStream,
pub compression_threshold: Option<u32>,
+ pub cipher: Option<Aes128Cfb>,
}
impl HandshakeConnection {
@@ -65,6 +68,7 @@ impl HandshakeConnection {
flow: self.flow,
stream: self.stream,
compression_threshold: None,
+ cipher: None,
}
}
@@ -76,46 +80,69 @@ impl HandshakeConnection {
}
pub async fn read(&mut self) -> Result<HandshakePacket, String> {
- read_packet::<HandshakePacket, _>(&self.flow, &mut self.stream, None).await
+ read_packet::<HandshakePacket, _>(&self.flow, &mut self.stream, None, &mut None).await
}
/// Write a packet to the server
pub async fn write(&mut self, packet: HandshakePacket) {
- write_packet(packet, &mut self.stream, None).await;
+ write_packet(packet, &mut self.stream, None, &mut None).await;
}
}
impl GameConnection {
pub async fn read(&mut self) -> Result<GamePacket, String> {
- read_packet::<GamePacket, _>(&self.flow, &mut self.stream, self.compression_threshold).await
+ read_packet::<GamePacket, _>(
+ &self.flow,
+ &mut self.stream,
+ self.compression_threshold,
+ &mut self.cipher,
+ )
+ .await
}
/// Write a packet to the server
pub async fn write(&mut self, packet: GamePacket) {
- write_packet(packet, &mut self.stream, self.compression_threshold).await;
+ write_packet(
+ packet,
+ &mut self.stream,
+ self.compression_threshold,
+ &mut self.cipher,
+ )
+ .await;
}
}
impl StatusConnection {
pub async fn read(&mut self) -> Result<StatusPacket, String> {
- read_packet::<StatusPacket, _>(&self.flow, &mut self.stream, None).await
+ read_packet::<StatusPacket, _>(&self.flow, &mut self.stream, None, &mut None).await
}
/// Write a packet to the server
pub async fn write(&mut self, packet: StatusPacket) {
- write_packet(packet, &mut self.stream, None).await;
+ write_packet(packet, &mut self.stream, None, &mut None).await;
}
}
impl LoginConnection {
pub async fn read(&mut self) -> Result<LoginPacket, String> {
- read_packet::<LoginPacket, _>(&self.flow, &mut self.stream, self.compression_threshold)
- .await
+ read_packet::<LoginPacket, _>(
+ &self.flow,
+ &mut self.stream,
+ self.compression_threshold,
+ &mut self.cipher,
+ )
+ .await
}
/// Write a packet to the server
pub async fn write(&mut self, packet: LoginPacket) {
- write_packet(packet, &mut self.stream, self.compression_threshold).await;
+ write_packet(
+ packet,
+ &mut self.stream,
+ self.compression_threshold,
+ &mut self.cipher,
+ )
+ .await;
}
pub fn set_compression_threshold(&mut self, threshold: i32) {
@@ -127,11 +154,18 @@ impl LoginConnection {
}
}
+ pub fn set_encryption_key(&mut self, key: [u8; 16]) {
+ // minecraft has a cipher decoder and encoder, i don't think it matters though?
+ let cipher = azalea_auth::encryption::create_cipher(&key);
+ self.cipher = Some(cipher);
+ }
+
pub fn game(self) -> GameConnection {
GameConnection {
flow: self.flow,
stream: self.stream,
compression_threshold: self.compression_threshold,
+ cipher: self.cipher,
}
}
}
diff --git a/azalea-protocol/src/mc_buf/mod.rs b/azalea-protocol/src/mc_buf/mod.rs
index 4ecb65d1..3ba6ac3e 100755
--- a/azalea-protocol/src/mc_buf/mod.rs
+++ b/azalea-protocol/src/mc_buf/mod.rs
@@ -5,11 +5,31 @@ mod write;
pub use read::{McBufReadable, McBufVarintReadable, Readable};
pub use write::{McBufVarintWritable, McBufWritable, Writable};
+use std::ops::Deref;
// const DEFAULT_NBT_QUOTA: u32 = 2097152;
const MAX_STRING_LENGTH: u16 = 32767;
// const MAX_COMPONENT_STRING_LENGTH: u32 = 262144;
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct ByteArray(Vec<u8>);
+
+impl Deref for ByteArray {
+ type Target = Vec<u8>;
+
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+
+impl From<Vec<u8>> for ByteArray {
+ fn from(vec: Vec<u8>) -> Self {
+ Self(vec)
+ }
+}
+
+
#[cfg(test)]
mod tests {
use super::*;
diff --git a/azalea-protocol/src/mc_buf/read.rs b/azalea-protocol/src/mc_buf/read.rs
index 1e031916..3d50e5aa 100755
--- a/azalea-protocol/src/mc_buf/read.rs
+++ b/azalea-protocol/src/mc_buf/read.rs
@@ -5,6 +5,7 @@ use azalea_core::{
};
use serde::Deserialize;
use tokio::io::{AsyncRead, AsyncReadExt};
+use crate::mc_buf::ByteArray;
use super::MAX_STRING_LENGTH;
@@ -14,7 +15,7 @@ pub trait Readable {
async fn read_varint(&mut self) -> Result<i32, String>;
fn get_varint_size(&mut self, value: i32) -> u8;
fn get_varlong_size(&mut self, value: i32) -> u8;
- async fn read_byte_array(&mut self) -> Result<Vec<u8>, String>;
+ async fn read_byte_array(&mut self) -> Result<ByteArray, String>;
async fn read_bytes_with_len(&mut self, n: usize) -> Result<Vec<u8>, String>;
async fn read_bytes(&mut self) -> Result<Vec<u8>, String>;
async fn read_utf(&mut self) -> Result<String, String>;
@@ -80,9 +81,9 @@ where
10
}
- async fn read_byte_array(&mut self) -> Result<Vec<u8>, String> {
+ async fn read_byte_array(&mut self) -> Result<ByteArray, String> {
let length = self.read_varint().await? as usize;
- Ok(self.read_bytes_with_len(length).await?)
+ Ok(ByteArray(self.read_bytes_with_len(length).await?))
}
async fn read_bytes_with_len(&mut self, n: usize) -> Result<Vec<u8>, String> {
@@ -251,6 +252,17 @@ impl McBufReadable for Vec<u8> {
}
}
+
+#[async_trait]
+impl McBufReadable for ByteArray {
+ async fn read_into<R>(buf: &mut R) -> Result<Self, String>
+ where
+ R: AsyncRead + std::marker::Unpin + std::marker::Send,
+ {
+ buf.read_byte_array().await
+ }
+}
+
// string
#[async_trait]
impl McBufReadable for String {
diff --git a/azalea-protocol/src/mc_buf/write.rs b/azalea-protocol/src/mc_buf/write.rs
index 05f613d8..f1362402 100755
--- a/azalea-protocol/src/mc_buf/write.rs
+++ b/azalea-protocol/src/mc_buf/write.rs
@@ -1,3 +1,5 @@
+use super::MAX_STRING_LENGTH;
+use crate::mc_buf::ByteArray;
use async_trait::async_trait;
use azalea_chat::component::Component;
use azalea_core::{
@@ -6,8 +8,6 @@ use azalea_core::{
use byteorder::{BigEndian, WriteBytesExt};
use std::io::Write;
-use super::MAX_STRING_LENGTH;
-
#[async_trait]
pub trait Writable {
fn write_list<F, T>(&mut self, list: &[T], writer: F) -> Result<(), std::io::Error>
@@ -193,6 +193,12 @@ impl McBufWritable for Vec<u8> {
}
}
+impl McBufWritable for ByteArray {
+ fn write_into(&self, buf: &mut Vec<u8>) -> Result<(), std::io::Error> {
+ buf.write_byte_array(&self)
+ }
+}
+
// string
impl McBufWritable for String {
fn write_into(&self, buf: &mut Vec<u8>) -> Result<(), std::io::Error> {
diff --git a/azalea-protocol/src/packets/login/clientbound_hello_packet.rs b/azalea-protocol/src/packets/login/clientbound_hello_packet.rs
index 9d0cec39..d36f1335 100755
--- a/azalea-protocol/src/packets/login/clientbound_hello_packet.rs
+++ b/azalea-protocol/src/packets/login/clientbound_hello_packet.rs
@@ -1,14 +1,13 @@
use std::hash::Hash;
-use crate::mc_buf::Readable;
-
use super::LoginPacket;
+use crate::mc_buf::{ByteArray, Readable};
#[derive(Hash, Clone, Debug)]
pub struct ClientboundHelloPacket {
pub server_id: String,
- pub public_key: Vec<u8>,
- pub nonce: Vec<u8>,
+ pub public_key: ByteArray,
+ pub nonce: ByteArray,
}
impl ClientboundHelloPacket {
diff --git a/azalea-protocol/src/packets/login/mod.rs b/azalea-protocol/src/packets/login/mod.rs
index ab68518c..b160a28c 100755
--- a/azalea-protocol/src/packets/login/mod.rs
+++ b/azalea-protocol/src/packets/login/mod.rs
@@ -4,6 +4,7 @@ pub mod clientbound_hello_packet;
pub mod clientbound_login_compression_packet;
pub mod clientbound_login_disconnect_packet;
pub mod serverbound_hello_packet;
+pub mod serverbound_key_packet;
use packet_macros::declare_state_packets;
@@ -11,10 +12,13 @@ declare_state_packets!(
LoginPacket,
Serverbound => {
0x00: serverbound_hello_packet::ServerboundHelloPacket,
+ 0x01: serverbound_key_packet::ServerboundKeyPacket,
},
Clientbound => {
- // 0x00: clientbound_login_disconnect_packet::ClientboundLoginDisconnectPacket,
- 26: clientbound_login_disconnect_packet::ClientboundLoginDisconnectPacket,
+ 0x00: clientbound_login_disconnect_packet::ClientboundLoginDisconnectPacket,
+ // sometimes this is used for some reason?
+ // 0x1a: clientbound_login_disconnect_packet::ClientboundLoginDisconnectPacket,
+
0x01: clientbound_hello_packet::ClientboundHelloPacket,
0x02: clientbound_game_profile_packet::ClientboundGameProfilePacket,
0x03: clientbound_login_compression_packet::ClientboundLoginCompressionPacket,
diff --git a/azalea-protocol/src/packets/login/serverbound_hello_packet.rs b/azalea-protocol/src/packets/login/serverbound_hello_packet.rs
index a72480f2..eb6a4065 100755
--- a/azalea-protocol/src/packets/login/serverbound_hello_packet.rs
+++ b/azalea-protocol/src/packets/login/serverbound_hello_packet.rs
@@ -1,27 +1,7 @@
+use packet_macros::LoginPacket;
use std::hash::Hash;
-use crate::mc_buf::Writable;
-
-use super::LoginPacket;
-
-#[derive(Hash, Clone, Debug)]
+#[derive(Hash, Clone, Debug, LoginPacket)]
pub struct ServerboundHelloPacket {
pub username: String,
}
-
-impl ServerboundHelloPacket {
- pub fn get(self) -> LoginPacket {
- LoginPacket::ServerboundHelloPacket(self)
- }
-
- pub fn write(&self, buf: &mut Vec<u8>) -> Result<(), std::io::Error> {
- buf.write_utf(&self.username).unwrap();
- Ok(())
- }
-
- pub async fn read<T: tokio::io::AsyncRead + std::marker::Unpin + std::marker::Send>(
- _buf: &mut T,
- ) -> Result<LoginPacket, String> {
- Err("ServerboundHelloPacket::read not implemented".to_string())
- }
-}
diff --git a/azalea-protocol/src/packets/login/serverbound_key_packet.rs b/azalea-protocol/src/packets/login/serverbound_key_packet.rs
new file mode 100644
index 00000000..3750331f
--- /dev/null
+++ b/azalea-protocol/src/packets/login/serverbound_key_packet.rs
@@ -0,0 +1,10 @@
+use super::LoginPacket;
+use crate::mc_buf::{ByteArray, Writable};
+use packet_macros::LoginPacket;
+use std::hash::Hash;
+
+#[derive(Hash, Clone, Debug, LoginPacket)]
+pub struct ServerboundKeyPacket {
+ pub shared_secret: ByteArray,
+ pub nonce: ByteArray,
+}
diff --git a/azalea-protocol/src/read.rs b/azalea-protocol/src/read.rs
index ed36d6b6..be20ac23 100755
--- a/azalea-protocol/src/read.rs
+++ b/azalea-protocol/src/read.rs
@@ -1,13 +1,17 @@
+use std::{cell::Cell, pin::Pin};
+
use crate::{connect::PacketFlow, mc_buf::Readable, packets::ProtocolPacket};
use async_compression::tokio::bufread::ZlibDecoder;
+use azalea_auth::encryption::Aes128Cfb;
use tokio::io::{AsyncRead, AsyncReadExt};
-async fn frame_splitter<R>(stream: &mut R) -> Result<Vec<u8>, String>
+async fn frame_splitter<R: ?Sized>(mut stream: &mut R) -> Result<Vec<u8>, String>
where
R: AsyncRead + std::marker::Unpin + std::marker::Send,
{
// Packet Length
let length_result = stream.read_varint().await;
+ println!("length_result: {:?}", length_result);
match length_result {
Ok(length) => {
let mut buf = vec![0; length as usize];
@@ -17,6 +21,8 @@ where
.await
.map_err(|e| e.to_string())?;
+ println!("buf: {:?}", buf);
+
Ok(buf)
}
Err(_) => Err("length wider than 21-bit".to_string()),
@@ -90,15 +96,61 @@ where
Ok(decoded_buf)
}
-pub async fn read_packet<P: ProtocolPacket, R>(
+struct EncryptedStream<'a, R>
+where
+ R: AsyncRead + std::marker::Unpin + std::marker::Send,
+{
+ cipher: Cell<&'a mut Option<Aes128Cfb>>,
+ stream: &'a mut Pin<&'a mut R>,
+}
+
+impl<R> AsyncRead for EncryptedStream<'_, R>
+where
+ R: AsyncRead + std::marker::Unpin + std::marker::Send,
+{
+ fn poll_read(
+ mut self: Pin<&mut Self>,
+ cx: &mut std::task::Context<'_>,
+ buf: &mut tokio::io::ReadBuf<'_>,
+ ) -> std::task::Poll<std::io::Result<()>> {
+ // i hate this
+ let polled = self.as_mut().stream.as_mut().poll_read(cx, buf);
+ match polled {
+ std::task::Poll::Ready(r) => {
+ println!("encrypted packet {:?}", buf.initialized_mut());
+ if let Some(cipher) = self.as_mut().cipher.get_mut() {
+ azalea_auth::encryption::decrypt_packet(cipher, buf.initialized_mut());
+ println!("decrypted packet {:?}", buf.initialized_mut());
+ }
+ match r {
+ Ok(()) => std::task::Poll::Ready(Ok(())),
+ Err(e) => panic!("{:?}", e),
+ }
+ }
+ std::task::Poll::Pending => {
+ return std::task::Poll::Pending;
+ }
+ }
+ }
+}
+
+pub async fn read_packet<'a, P: ProtocolPacket, R>(
flow: &PacketFlow,
- stream: &mut R,
+ stream: &'a mut R,
compression_threshold: Option<u32>,
+ cipher: &mut Option<Aes128Cfb>,
) -> Result<P, String>
where
- R: AsyncRead + std::marker::Unpin + std::marker::Send,
+ R: AsyncRead + std::marker::Unpin + std::marker::Send + std::marker::Sync,
{
- let mut buf = frame_splitter(stream).await?;
+ // if we were given a cipher, decrypt the packet
+ let mut encrypted_stream = EncryptedStream {
+ cipher: Cell::new(cipher),
+ stream: &mut Pin::new(stream),
+ };
+
+ let mut buf = frame_splitter(&mut encrypted_stream).await?;
+
if let Some(compression_threshold) = compression_threshold {
buf = compression_decoder(&mut buf.as_slice(), compression_threshold).await?;
}
diff --git a/azalea-protocol/src/write.rs b/azalea-protocol/src/write.rs
index 821ed6e8..e72a74b1 100755
--- a/azalea-protocol/src/write.rs
+++ b/azalea-protocol/src/write.rs
@@ -1,5 +1,6 @@
use crate::{mc_buf::Writable, packets::ProtocolPacket, read::MAXIMUM_UNCOMPRESSED_LENGTH};
use async_compression::tokio::bufread::ZlibEncoder;
+use azalea_auth::encryption::Aes128Cfb;
use tokio::{
io::{AsyncReadExt, AsyncWriteExt},
net::TcpStream,
@@ -50,14 +51,22 @@ async fn compression_encoder(data: &[u8], compression_threshold: u32) -> Result<
}
}
-pub async fn write_packet<P>(packet: P, stream: &mut TcpStream, compression_threshold: Option<u32>)
-where
+pub async fn write_packet<P>(
+ packet: P,
+ stream: &mut TcpStream,
+ compression_threshold: Option<u32>,
+ cipher: &mut Option<Aes128Cfb>,
+) where
P: ProtocolPacket + std::fmt::Debug,
{
let mut buf = packet_encoder(&packet).unwrap();
if let Some(threshold) = compression_threshold {
buf = compression_encoder(&buf, threshold).await.unwrap();
}
+ // if we were given a cipher, encrypt the packet
+ if let Some(cipher) = cipher {
+ azalea_auth::encryption::encrypt_packet(cipher, &mut buf);
+ }
buf = frame_prepender(&mut buf).unwrap();
stream.write_all(&buf).await.unwrap();
}
diff --git a/bot/src/main.rs b/bot/src/main.rs
index 7d129478..7d129478 100755..100644
--- a/bot/src/main.rs
+++ b/bot/src/main.rs