mirror of
https://github.com/mat-1/azalea.git
synced 2025-08-02 14:26:04 +00:00
add stuff related to chat signing
and also some stuff related to digging because i forgot to do a different branch lol
This commit is contained in:
parent
9bdace4aab
commit
6188230009
13 changed files with 500 additions and 28 deletions
160
Cargo.lock
generated
160
Cargo.lock
generated
|
@ -198,11 +198,14 @@ version = "0.7.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"azalea-buf",
|
"azalea-buf",
|
||||||
"azalea-crypto",
|
"azalea-crypto",
|
||||||
|
"base64",
|
||||||
"chrono",
|
"chrono",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
"log",
|
"log",
|
||||||
"num-bigint",
|
"num-bigint",
|
||||||
|
"parking_lot",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
|
"rsa",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
@ -282,6 +285,7 @@ dependencies = [
|
||||||
"azalea-core",
|
"azalea-core",
|
||||||
"azalea-crypto",
|
"azalea-crypto",
|
||||||
"azalea-inventory",
|
"azalea-inventory",
|
||||||
|
"azalea-nbt",
|
||||||
"azalea-physics",
|
"azalea-physics",
|
||||||
"azalea-protocol",
|
"azalea-protocol",
|
||||||
"azalea-registry",
|
"azalea-registry",
|
||||||
|
@ -328,8 +332,10 @@ dependencies = [
|
||||||
"criterion",
|
"criterion",
|
||||||
"num-bigint",
|
"num-bigint",
|
||||||
"rand",
|
"rand",
|
||||||
|
"rsa",
|
||||||
"rsa_public_encrypt_pkcs1",
|
"rsa_public_encrypt_pkcs1",
|
||||||
"sha-1",
|
"sha-1",
|
||||||
|
"sha2",
|
||||||
"uuid",
|
"uuid",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -502,9 +508,15 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "base64"
|
name = "base64"
|
||||||
version = "0.21.0"
|
version = "0.21.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a"
|
checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "base64ct"
|
||||||
|
version = "1.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bevy_app"
|
name = "bevy_app"
|
||||||
|
@ -802,6 +814,7 @@ checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"num-integer",
|
"num-integer",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -895,6 +908,12 @@ dependencies = [
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "const-oid"
|
||||||
|
version = "0.9.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "convert_case"
|
name = "convert_case"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
|
@ -1014,6 +1033,17 @@ version = "2.3.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "23d8666cb01533c39dde32bcbab8e227b4ed6679b2c925eba05feabea39508fb"
|
checksum = "23d8666cb01533c39dde32bcbab8e227b4ed6679b2c925eba05feabea39508fb"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "der"
|
||||||
|
version = "0.7.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "56acb310e15652100da43d130af8d97b509e95af61aab1c5a7939ef24337ee17"
|
||||||
|
dependencies = [
|
||||||
|
"const-oid",
|
||||||
|
"pem-rfc7468",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "derive_more"
|
name = "derive_more"
|
||||||
version = "0.99.17"
|
version = "0.99.17"
|
||||||
|
@ -1034,6 +1064,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f"
|
checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"block-buffer",
|
"block-buffer",
|
||||||
|
"const-oid",
|
||||||
"crypto-common",
|
"crypto-common",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -1541,6 +1572,9 @@ name = "lazy_static"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||||
|
dependencies = [
|
||||||
|
"spin",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
|
@ -1554,6 +1588,12 @@ version = "0.1.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7fc7aa29613bd6a620df431842069224d8bc9011086b1db4c0e0cd47fa03ec9a"
|
checksum = "7fc7aa29613bd6a620df431842069224d8bc9011086b1db4c0e0cd47fa03ec9a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libm"
|
||||||
|
version = "0.2.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linked-hash-map"
|
name = "linked-hash-map"
|
||||||
version = "0.5.6"
|
version = "0.5.6"
|
||||||
|
@ -1695,6 +1735,23 @@ dependencies = [
|
||||||
"num-traits",
|
"num-traits",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-bigint-dig"
|
||||||
|
version = "0.8.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2399c9463abc5f909349d8aa9ba080e0b88b3ce2885389b60b993f39b1a56905"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
"lazy_static",
|
||||||
|
"libm 0.2.7",
|
||||||
|
"num-integer",
|
||||||
|
"num-iter",
|
||||||
|
"num-traits",
|
||||||
|
"rand",
|
||||||
|
"smallvec",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-complex"
|
name = "num-complex"
|
||||||
version = "0.4.3"
|
version = "0.4.3"
|
||||||
|
@ -1744,6 +1801,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
|
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
|
"libm 0.2.7",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1796,7 +1854,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a1914cd452d8fccd6f9db48147b29fd4ae05bea9dc5d9ad578509f72415de282"
|
checksum = "a1914cd452d8fccd6f9db48147b29fd4ae05bea9dc5d9ad578509f72415de282"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libm",
|
"libm 0.1.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1831,6 +1889,15 @@ dependencies = [
|
||||||
"windows-sys 0.45.0",
|
"windows-sys 0.45.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pem-rfc7468"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412"
|
||||||
|
dependencies = [
|
||||||
|
"base64ct",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "percent-encoding"
|
name = "percent-encoding"
|
||||||
version = "2.2.0"
|
version = "2.2.0"
|
||||||
|
@ -1859,6 +1926,27 @@ version = "0.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pkcs1"
|
||||||
|
version = "0.7.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f"
|
||||||
|
dependencies = [
|
||||||
|
"der",
|
||||||
|
"pkcs8",
|
||||||
|
"spki",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pkcs8"
|
||||||
|
version = "0.10.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7"
|
||||||
|
dependencies = [
|
||||||
|
"der",
|
||||||
|
"spki",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "plotters"
|
name = "plotters"
|
||||||
version = "0.3.4"
|
version = "0.3.4"
|
||||||
|
@ -2068,6 +2156,29 @@ dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rsa"
|
||||||
|
version = "0.9.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6ab43bb47d23c1a631b4b680199a45255dce26fa9ab2fa902581f624ff13e6a8"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
"const-oid",
|
||||||
|
"digest",
|
||||||
|
"num-bigint-dig",
|
||||||
|
"num-integer",
|
||||||
|
"num-iter",
|
||||||
|
"num-traits",
|
||||||
|
"pkcs1",
|
||||||
|
"pkcs8",
|
||||||
|
"rand_core",
|
||||||
|
"sha2",
|
||||||
|
"signature",
|
||||||
|
"spki",
|
||||||
|
"subtle",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rsa_public_encrypt_pkcs1"
|
name = "rsa_public_encrypt_pkcs1"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
|
@ -2237,6 +2348,17 @@ dependencies = [
|
||||||
"digest",
|
"digest",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sha2"
|
||||||
|
version = "0.10.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"cpufeatures",
|
||||||
|
"digest",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sharded-slab"
|
name = "sharded-slab"
|
||||||
version = "0.1.4"
|
version = "0.1.4"
|
||||||
|
@ -2255,6 +2377,16 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "signature"
|
||||||
|
version = "2.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500"
|
||||||
|
dependencies = [
|
||||||
|
"digest",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "simple_asn1"
|
name = "simple_asn1"
|
||||||
version = "0.5.4"
|
version = "0.5.4"
|
||||||
|
@ -2301,12 +2433,28 @@ version = "0.5.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "spki"
|
||||||
|
version = "0.7.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a"
|
||||||
|
dependencies = [
|
||||||
|
"base64ct",
|
||||||
|
"der",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "static_assertions"
|
name = "static_assertions"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "subtle"
|
||||||
|
version = "2.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.109"
|
version = "1.0.109"
|
||||||
|
@ -3014,3 +3162,9 @@ dependencies = [
|
||||||
"quote",
|
"quote",
|
||||||
"syn 1.0.109",
|
"syn 1.0.109",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zeroize"
|
||||||
|
version = "1.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9"
|
||||||
|
|
|
@ -11,13 +11,16 @@ version = "0.7.0"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
azalea-buf = { path = "../azalea-buf", version = "^0.7.0" }
|
azalea-buf = { path = "../azalea-buf", version = "^0.7.0" }
|
||||||
azalea-crypto = { path = "../azalea-crypto", version = "^0.7.0" }
|
azalea-crypto = { path = "../azalea-crypto", version = "^0.7.0" }
|
||||||
chrono = { version = "0.4.22", default-features = false }
|
base64 = "0.21.2"
|
||||||
|
chrono = { version = "0.4.22", default-features = false, features = ["serde"] }
|
||||||
log = "0.4.17"
|
log = "0.4.17"
|
||||||
num-bigint = "0.4.3"
|
num-bigint = "0.4.3"
|
||||||
|
parking_lot = "0.12.1"
|
||||||
reqwest = { version = "0.11.12", default-features = false, features = [
|
reqwest = { version = "0.11.12", default-features = false, features = [
|
||||||
"json",
|
"json",
|
||||||
"rustls-tls",
|
"rustls-tls",
|
||||||
] }
|
] }
|
||||||
|
rsa = "0.9.2"
|
||||||
serde = { version = "^1.0", features = ["derive"] }
|
serde = { version = "^1.0", features = ["derive"] }
|
||||||
serde_json = "1.0.93"
|
serde_json = "1.0.93"
|
||||||
thiserror = "1.0.37"
|
thiserror = "1.0.37"
|
||||||
|
|
24
azalea-auth/examples/certificates.rs
Executable file
24
azalea-auth/examples/certificates.rs
Executable file
|
@ -0,0 +1,24 @@
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() {
|
||||||
|
env_logger::init();
|
||||||
|
|
||||||
|
let cache_file = PathBuf::from("example_cache.json");
|
||||||
|
|
||||||
|
let auth_result = azalea_auth::auth(
|
||||||
|
"example@example.com",
|
||||||
|
azalea_auth::AuthOpts {
|
||||||
|
cache_file: Some(cache_file),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let certs = azalea_auth::certs::fetch_certificates(&auth_result.access_token)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
println!("{certs:?}");
|
||||||
|
}
|
138
azalea-auth/src/certs.rs
Normal file
138
azalea-auth/src/certs.rs
Normal file
|
@ -0,0 +1,138 @@
|
||||||
|
use base64::Engine;
|
||||||
|
use chrono::{DateTime, Utc};
|
||||||
|
use rsa::{pkcs8::DecodePrivateKey, RsaPrivateKey};
|
||||||
|
use serde::Deserialize;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
#[derive(Debug, Error)]
|
||||||
|
pub enum FetchCertificatesError {
|
||||||
|
#[error("Http error: {0}")]
|
||||||
|
Http(#[from] reqwest::Error),
|
||||||
|
#[error("Couldn't parse pkcs8 private key: {0}")]
|
||||||
|
Pkcs8(#[from] rsa::pkcs8::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fetch the Mojang-provided key-pair for your player, which is used for
|
||||||
|
/// cryptographically signing chat messages.
|
||||||
|
pub async fn fetch_certificates(
|
||||||
|
minecraft_access_token: &str,
|
||||||
|
) -> Result<Certificates, FetchCertificatesError> {
|
||||||
|
let client = reqwest::Client::new();
|
||||||
|
|
||||||
|
let res = client
|
||||||
|
.post("https://api.minecraftservices.com/player/certificates")
|
||||||
|
.header("Authorization", format!("Bearer {minecraft_access_token}"))
|
||||||
|
.send()
|
||||||
|
.await?
|
||||||
|
.json::<CertificatesResponse>()
|
||||||
|
.await?;
|
||||||
|
log::trace!("{:?}", res);
|
||||||
|
|
||||||
|
// using RsaPrivateKey::from_pkcs8_pem gives an error with decoding base64 so we
|
||||||
|
// just decode it ourselves
|
||||||
|
|
||||||
|
// remove the first and last lines of the private key
|
||||||
|
let private_key_pem_base64 = res
|
||||||
|
.key_pair
|
||||||
|
.private_key
|
||||||
|
.lines()
|
||||||
|
.skip(1)
|
||||||
|
.take_while(|line| !line.starts_with('-'))
|
||||||
|
.collect::<String>();
|
||||||
|
let private_key_der = base64::engine::general_purpose::STANDARD
|
||||||
|
.decode(private_key_pem_base64)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let public_key_pem_base64 = res
|
||||||
|
.key_pair
|
||||||
|
.public_key
|
||||||
|
.lines()
|
||||||
|
.skip(1)
|
||||||
|
.take_while(|line| !line.starts_with('-'))
|
||||||
|
.collect::<String>();
|
||||||
|
let public_key_der = base64::engine::general_purpose::STANDARD
|
||||||
|
.decode(public_key_pem_base64)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// the private key also contains the public key so it's basically a keypair
|
||||||
|
let key_pair = RsaPrivateKey::from_pkcs8_der(&private_key_der).unwrap();
|
||||||
|
|
||||||
|
let certificates = Certificates {
|
||||||
|
key_pair,
|
||||||
|
key_pair_bytes: KeyPairBytes {
|
||||||
|
private_key: private_key_der,
|
||||||
|
public_key: public_key_der,
|
||||||
|
},
|
||||||
|
|
||||||
|
signature_v1: base64::engine::general_purpose::STANDARD
|
||||||
|
.decode(&res.public_key_signature)
|
||||||
|
.unwrap(),
|
||||||
|
signature_v2: base64::engine::general_purpose::STANDARD
|
||||||
|
.decode(&res.public_key_signature_v2)
|
||||||
|
.unwrap(),
|
||||||
|
|
||||||
|
expires_at: res.expires_at,
|
||||||
|
refresh_after: res.refreshed_after,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(certificates)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A chat signing certificate.
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Certificates {
|
||||||
|
/// The RSA private and public key.
|
||||||
|
pub key_pair: RsaPrivateKey,
|
||||||
|
/// The keypair as DER-encoded bytes.
|
||||||
|
pub key_pair_bytes: KeyPairBytes,
|
||||||
|
|
||||||
|
pub signature_v1: Vec<u8>,
|
||||||
|
pub signature_v2: Vec<u8>,
|
||||||
|
|
||||||
|
pub expires_at: DateTime<Utc>,
|
||||||
|
pub refresh_after: DateTime<Utc>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A keypair as DER-encoded bytes.
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct KeyPairBytes {
|
||||||
|
pub private_key: Vec<u8>,
|
||||||
|
pub public_key: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct CertificatesResponse {
|
||||||
|
#[serde(rename = "keyPair")]
|
||||||
|
pub key_pair: KeyPairResponse,
|
||||||
|
|
||||||
|
/// base64 string; signed data
|
||||||
|
#[serde(rename = "publicKeySignature")]
|
||||||
|
pub public_key_signature: String,
|
||||||
|
|
||||||
|
/// base64 string; signed data
|
||||||
|
#[serde(rename = "publicKeySignatureV2")]
|
||||||
|
pub public_key_signature_v2: String,
|
||||||
|
|
||||||
|
/// Date like `2022-04-30T00:11:32.174783069Z`
|
||||||
|
#[serde(rename = "expiresAt")]
|
||||||
|
pub expires_at: DateTime<Utc>,
|
||||||
|
|
||||||
|
/// Date like `2022-04-29T16:11:32.174783069Z`
|
||||||
|
#[serde(rename = "refreshedAfter")]
|
||||||
|
pub refreshed_after: DateTime<Utc>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct KeyPairResponse {
|
||||||
|
/// -----BEGIN RSA PRIVATE KEY-----
|
||||||
|
/// ...
|
||||||
|
/// -----END RSA PRIVATE KEY-----
|
||||||
|
#[serde(rename = "privateKey")]
|
||||||
|
pub private_key: String,
|
||||||
|
|
||||||
|
/// -----BEGIN RSA PUBLIC KEY-----
|
||||||
|
/// ...
|
||||||
|
/// -----END RSA PUBLIC KEY-----
|
||||||
|
#[serde(rename = "publicKey")]
|
||||||
|
pub public_key: String,
|
||||||
|
}
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
mod auth;
|
mod auth;
|
||||||
mod cache;
|
mod cache;
|
||||||
|
pub mod certs;
|
||||||
pub mod game_profile;
|
pub mod game_profile;
|
||||||
pub mod sessionserver;
|
pub mod sessionserver;
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@ anyhow = "1.0.59"
|
||||||
async-trait = "0.1.58"
|
async-trait = "0.1.58"
|
||||||
azalea-auth = { path = "../azalea-auth", version = "0.7.0" }
|
azalea-auth = { path = "../azalea-auth", version = "0.7.0" }
|
||||||
azalea-block = { path = "../azalea-block", version = "0.7.0" }
|
azalea-block = { path = "../azalea-block", version = "0.7.0" }
|
||||||
|
azalea-nbt = { path = "../azalea-nbt", version = "0.7.0" }
|
||||||
azalea-chat = { path = "../azalea-chat", version = "0.7.0" }
|
azalea-chat = { path = "../azalea-chat", version = "0.7.0" }
|
||||||
azalea-core = { path = "../azalea-core", version = "0.7.0" }
|
azalea-core = { path = "../azalea-core", version = "0.7.0" }
|
||||||
azalea-crypto = { path = "../azalea-crypto", version = "0.7.0" }
|
azalea-crypto = { path = "../azalea-crypto", version = "0.7.0" }
|
||||||
|
|
|
@ -3,7 +3,9 @@
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::get_mc_dir;
|
use crate::get_mc_dir;
|
||||||
|
use azalea_auth::certs::{Certificates, FetchCertificatesError};
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
use thiserror::Error;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
/// Something that can join Minecraft servers.
|
/// Something that can join Minecraft servers.
|
||||||
|
@ -38,17 +40,22 @@ pub struct Account {
|
||||||
pub uuid: Option<Uuid>,
|
pub uuid: Option<Uuid>,
|
||||||
|
|
||||||
/// The parameters (i.e. email) that were passed for creating this
|
/// The parameters (i.e. email) that were passed for creating this
|
||||||
/// [`Account`]. This is used to for automatic reauthentication when we get
|
/// [`Account`]. This is used for automatic reauthentication when we get
|
||||||
/// "Invalid Session" errors. If you don't need that feature (like in
|
/// "Invalid Session" errors. If you don't need that feature (like in
|
||||||
/// offline mode), then you can set this to `AuthOpts::default()`.
|
/// offline mode), then you can set this to `AuthOpts::default()`.
|
||||||
pub account_opts: AccountOpts,
|
pub account_opts: AccountOpts,
|
||||||
|
|
||||||
|
/// The certificates used for chat signing.
|
||||||
|
///
|
||||||
|
/// This is set when you call [`Self::request_certs`], but you only
|
||||||
|
/// need to if the servers you're joining require it.
|
||||||
|
pub certs: Option<Certificates>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The parameters that were passed for creating the associated [`Account`].
|
/// The parameters that were passed for creating the associated [`Account`].
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum AccountOpts {
|
pub enum AccountOpts {
|
||||||
Offline { username: String },
|
Offline { username: String },
|
||||||
// this is an enum so legacy Mojang auth can be added in the future
|
|
||||||
Microsoft { email: String },
|
Microsoft { email: String },
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,6 +71,7 @@ impl Account {
|
||||||
account_opts: AccountOpts::Offline {
|
account_opts: AccountOpts::Offline {
|
||||||
username: username.to_string(),
|
username: username.to_string(),
|
||||||
},
|
},
|
||||||
|
certs: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,6 +101,8 @@ impl Account {
|
||||||
account_opts: AccountOpts::Microsoft {
|
account_opts: AccountOpts::Microsoft {
|
||||||
email: email.to_string(),
|
email: email.to_string(),
|
||||||
},
|
},
|
||||||
|
// we don't do chat signing by default unless the user asks for it
|
||||||
|
certs: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,3 +132,29 @@ impl Account {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum RequestCertError {
|
||||||
|
#[error("Failed to fetch certificates")]
|
||||||
|
FetchCertificates(#[from] FetchCertificatesError),
|
||||||
|
#[error("You can't request certificates for an offline account")]
|
||||||
|
NoAccessToken,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Account {
|
||||||
|
/// Request the certificates used for chat signing and set it in
|
||||||
|
/// [`Self::certs`].
|
||||||
|
pub async fn request_certs(&mut self) -> Result<(), RequestCertError> {
|
||||||
|
let certs = azalea_auth::certs::fetch_certificates(
|
||||||
|
&self
|
||||||
|
.access_token
|
||||||
|
.as_ref()
|
||||||
|
.ok_or(RequestCertError::NoAccessToken)?
|
||||||
|
.lock(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
self.certs = Some(certs);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
|
use azalea_block::BlockState;
|
||||||
use azalea_core::{BlockHitResult, BlockPos, Direction, GameMode, Vec3};
|
use azalea_core::{BlockHitResult, BlockPos, Direction, GameMode, Vec3};
|
||||||
|
use azalea_inventory::{ItemSlot, ItemSlotData};
|
||||||
|
use azalea_nbt::NbtList;
|
||||||
use azalea_physics::clip::{BlockShapeType, ClipContext, FluidPickType};
|
use azalea_physics::clip::{BlockShapeType, ClipContext, FluidPickType};
|
||||||
use azalea_protocol::packets::game::{
|
use azalea_protocol::packets::game::{
|
||||||
serverbound_interact_packet::InteractionHand,
|
serverbound_interact_packet::InteractionHand,
|
||||||
|
@ -20,6 +23,8 @@ use derive_more::{Deref, DerefMut};
|
||||||
use log::warn;
|
use log::warn;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
client::PlayerAbilities,
|
||||||
|
inventory::InventoryComponent,
|
||||||
local_player::{handle_send_packet_event, LocalGameMode},
|
local_player::{handle_send_packet_event, LocalGameMode},
|
||||||
Client, LocalPlayer,
|
Client, LocalPlayer,
|
||||||
};
|
};
|
||||||
|
@ -193,3 +198,68 @@ pub fn pick(
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether we can't interact with the block, based on your gamemode. If
|
||||||
|
/// this is false, then we can interact with the block.
|
||||||
|
///
|
||||||
|
/// Passing the inventory, block position, and instance is necessary for the
|
||||||
|
/// adventure mode check.
|
||||||
|
pub fn check_is_interaction_restricted(
|
||||||
|
instance: &Instance,
|
||||||
|
block_pos: &BlockPos,
|
||||||
|
game_mode: &GameMode,
|
||||||
|
inventory: &InventoryComponent,
|
||||||
|
) -> bool {
|
||||||
|
match game_mode {
|
||||||
|
GameMode::Adventure => {
|
||||||
|
// vanilla checks for abilities.mayBuild here but servers have no
|
||||||
|
// way of modifying that
|
||||||
|
|
||||||
|
let held_item = inventory.held_item();
|
||||||
|
if let ItemSlot::Present(item) = &held_item {
|
||||||
|
let block = instance.chunks.get_block_state(block_pos);
|
||||||
|
let Some(block) = block else {
|
||||||
|
// block isn't loaded so just say that it is restricted
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
check_block_can_be_broken_by_item_in_adventure_mode(item, &block)
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GameMode::Spectator => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if the item has the `CanDestroy` tag for the block.
|
||||||
|
pub fn check_block_can_be_broken_by_item_in_adventure_mode(
|
||||||
|
item: &ItemSlotData,
|
||||||
|
block: &BlockState,
|
||||||
|
) -> bool {
|
||||||
|
// minecraft caches the last checked block but that's kind of an unnecessary
|
||||||
|
// optimization and makes the code too complicated
|
||||||
|
|
||||||
|
let Some(can_destroy) = item
|
||||||
|
.nbt
|
||||||
|
.as_compound()
|
||||||
|
.and_then(|nbt| nbt.get("tag").and_then(|nbt| nbt.as_compound()))
|
||||||
|
.and_then(|nbt| nbt.get("CanDestroy").and_then(|nbt| nbt.as_list())) else {
|
||||||
|
// no CanDestroy tag
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
let NbtList::String(can_destroy) = can_destroy else {
|
||||||
|
// CanDestroy tag must be a list of strings
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// for block_predicate in can_destroy {
|
||||||
|
// // TODO
|
||||||
|
// // defined in BlockPredicateArgument.java
|
||||||
|
// }
|
||||||
|
|
||||||
|
// true
|
||||||
|
}
|
||||||
|
|
|
@ -78,6 +78,9 @@ pub struct InventoryComponent {
|
||||||
pub container_menu: Option<azalea_inventory::Menu>,
|
pub container_menu: Option<azalea_inventory::Menu>,
|
||||||
/// The item that is currently held by the cursor. `Slot::Empty` if nothing
|
/// The item that is currently held by the cursor. `Slot::Empty` if nothing
|
||||||
/// is currently being held.
|
/// is currently being held.
|
||||||
|
///
|
||||||
|
/// This is different from [`Self::hotbar_selected_index`], which is the
|
||||||
|
/// item that's selected in the hotbar.
|
||||||
pub carried: ItemSlot,
|
pub carried: ItemSlot,
|
||||||
/// An identifier used by the server to track client inventory desyncs. This
|
/// An identifier used by the server to track client inventory desyncs. This
|
||||||
/// is sent on every container click, and it's only ever updated when the
|
/// is sent on every container click, and it's only ever updated when the
|
||||||
|
@ -89,12 +92,13 @@ pub struct InventoryComponent {
|
||||||
/// A set of the indexes of the slots that have been right clicked in
|
/// A set of the indexes of the slots that have been right clicked in
|
||||||
/// this "quick craft".
|
/// this "quick craft".
|
||||||
pub quick_craft_slots: HashSet<u16>,
|
pub quick_craft_slots: HashSet<u16>,
|
||||||
// minecraft also has these fields, but i don't
|
|
||||||
// think they're necessary?:
|
/// The index of the item in the hotbar that's currently being held by the
|
||||||
// private final NonNullList<ItemStack>
|
/// player. This MUST be in the range 0..9 (not including 9).
|
||||||
// remoteSlots;
|
///
|
||||||
// private final IntList remoteDataSlots;
|
/// In a vanilla client this is changed by pressing the number keys or using
|
||||||
// private ItemStack remoteCarried;
|
/// the scroll wheel.
|
||||||
|
pub selected_hotbar_slot: u8,
|
||||||
}
|
}
|
||||||
impl InventoryComponent {
|
impl InventoryComponent {
|
||||||
/// Returns a reference to the currently active menu. If a container is open
|
/// Returns a reference to the currently active menu. If a container is open
|
||||||
|
@ -272,7 +276,6 @@ impl InventoryComponent {
|
||||||
};
|
};
|
||||||
*menu.slot_mut(slot_index as usize).unwrap() =
|
*menu.slot_mut(slot_index as usize).unwrap() =
|
||||||
ItemSlot::Present(new_carried);
|
ItemSlot::Present(new_carried);
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -493,6 +496,13 @@ impl InventoryComponent {
|
||||||
self.quick_craft_status = QuickCraftStatusKind::Start;
|
self.quick_craft_status = QuickCraftStatusKind::Start;
|
||||||
self.quick_craft_slots.clear();
|
self.quick_craft_slots.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the item in the player's hotbar that is currently being held.
|
||||||
|
pub fn held_item(&self) -> ItemSlot {
|
||||||
|
let inventory = &self.inventory_menu;
|
||||||
|
let hotbar_items = &inventory.slots()[inventory.hotbar_slots_range()];
|
||||||
|
hotbar_items[self.selected_hotbar_slot as usize].clone()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn can_item_quick_replace(
|
fn can_item_quick_replace(
|
||||||
|
@ -521,21 +531,6 @@ fn can_item_quick_replace(
|
||||||
count <= item.kind.max_stack_size() as u16
|
count <= item.kind.max_stack_size() as u16
|
||||||
}
|
}
|
||||||
|
|
||||||
// public static void getQuickCraftSlotCount(Set<Slot> quickCraftSlots, int
|
|
||||||
// quickCraftType, ItemStack itemStack, int var3) {
|
|
||||||
// switch (quickCraftType) {
|
|
||||||
// case 0:
|
|
||||||
// itemStack.setCount(Mth.floor((float) itemStack.getCount() / (float)
|
|
||||||
// quickCraftSlots.size())); break;
|
|
||||||
// case 1:
|
|
||||||
// itemStack.setCount(1);
|
|
||||||
// break;
|
|
||||||
// case 2:
|
|
||||||
// itemStack.setCount(itemStack.getItem().getMaxStackSize());
|
|
||||||
// }
|
|
||||||
|
|
||||||
// itemStack.grow(var3);
|
|
||||||
// }
|
|
||||||
fn get_quick_craft_slot_count(
|
fn get_quick_craft_slot_count(
|
||||||
quick_craft_slots: &HashSet<u16>,
|
quick_craft_slots: &HashSet<u16>,
|
||||||
quick_craft_kind: &QuickCraftKind,
|
quick_craft_kind: &QuickCraftKind,
|
||||||
|
@ -561,6 +556,7 @@ impl Default for InventoryComponent {
|
||||||
quick_craft_status: QuickCraftStatusKind::Start,
|
quick_craft_status: QuickCraftStatusKind::Start,
|
||||||
quick_craft_kind: QuickCraftKind::Middle,
|
quick_craft_kind: QuickCraftKind::Middle,
|
||||||
quick_craft_slots: HashSet::new(),
|
quick_craft_slots: HashSet::new(),
|
||||||
|
selected_hotbar_slot: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ mod get_mc_dir;
|
||||||
pub mod interact;
|
pub mod interact;
|
||||||
pub mod inventory;
|
pub mod inventory;
|
||||||
mod local_player;
|
mod local_player;
|
||||||
|
mod mining;
|
||||||
mod movement;
|
mod movement;
|
||||||
pub mod packet_handling;
|
pub mod packet_handling;
|
||||||
pub mod ping;
|
pub mod ping;
|
||||||
|
|
35
azalea-client/src/mining.rs
Normal file
35
azalea-client/src/mining.rs
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
use azalea_core::BlockPos;
|
||||||
|
use bevy_app::{App, Plugin};
|
||||||
|
use bevy_ecs::prelude::*;
|
||||||
|
|
||||||
|
use crate::Client;
|
||||||
|
|
||||||
|
/// A plugin that allows clients to break blocks in the world.
|
||||||
|
pub struct MinePlugin;
|
||||||
|
impl Plugin for MinePlugin {
|
||||||
|
fn build(&self, app: &mut App) {
|
||||||
|
app.add_event::<StartMiningBlockEvent>()
|
||||||
|
.add_system(handle_start_mining_block_event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Client {
|
||||||
|
/// Start mining a block.
|
||||||
|
pub fn start_mining_block(&self, position: BlockPos) {
|
||||||
|
self.ecs.lock().send_event(StartMiningBlockEvent {
|
||||||
|
entity: self.entity,
|
||||||
|
position,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct StartMiningBlockEvent {
|
||||||
|
pub entity: Entity,
|
||||||
|
pub position: BlockPos,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_start_mining_block_event(mut events: EventReader<StartMiningBlockEvent>) {
|
||||||
|
for event in events.iter() {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
|
@ -82,6 +82,15 @@ impl GameMode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl GameMode {
|
||||||
|
/// Whether the player can't interact with blocks while in this game mode.
|
||||||
|
///
|
||||||
|
/// (Returns true if you're in adventure or spectator.)
|
||||||
|
pub fn is_block_placing_restricted(&self) -> bool {
|
||||||
|
matches!(self, GameMode::Adventure | GameMode::Spectator)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl McBufReadable for GameMode {
|
impl McBufReadable for GameMode {
|
||||||
fn read_from(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
|
fn read_from(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
|
||||||
let id = u8::read_from(buf)?;
|
let id = u8::read_from(buf)?;
|
||||||
|
|
|
@ -177,6 +177,10 @@ pub fn generate(input: &DeclareMenus) -> TokenStream {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the range of slot indexes that contain the player's hotbar. This may be different for each menu.
|
/// Get the range of slot indexes that contain the player's hotbar. This may be different for each menu.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// let hotbar_items = &inventory.slots()[inventory.hotbar_slots_range()];
|
||||||
|
/// ```
|
||||||
pub fn hotbar_slots_range(&self) -> RangeInclusive<usize> {
|
pub fn hotbar_slots_range(&self) -> RangeInclusive<usize> {
|
||||||
// hotbar is always last 9 slots in the player's inventory
|
// hotbar is always last 9 slots in the player's inventory
|
||||||
((*self.player_slots_range().end() - 8)..=*self.player_slots_range().end())
|
((*self.player_slots_range().end() - 8)..=*self.player_slots_range().end())
|
||||||
|
|
Loading…
Add table
Reference in a new issue