aboutsummaryrefslogtreecommitdiff
path: root/azalea-auth/src
diff options
context:
space:
mode:
authormat <git@matdoes.dev>2023-05-26 15:18:04 -0500
committermat <git@matdoes.dev>2023-05-26 15:18:04 -0500
commit6188230009b49f96b755ade32a28b932e7810196 (patch)
treef7b6bc8e25dfda27f3162f9e6bd53721fb3a86cc /azalea-auth/src
parent9bdace4aab064257dccb39fab4d47fde6dd9a062 (diff)
downloadazalea-drasl-6188230009b49f96b755ade32a28b932e7810196.tar.xz
add stuff related to chat signing
and also some stuff related to digging because i forgot to do a different branch lol
Diffstat (limited to 'azalea-auth/src')
-rw-r--r--azalea-auth/src/certs.rs138
-rwxr-xr-xazalea-auth/src/lib.rs1
2 files changed, 139 insertions, 0 deletions
diff --git a/azalea-auth/src/certs.rs b/azalea-auth/src/certs.rs
new file mode 100644
index 00000000..809a10c6
--- /dev/null
+++ b/azalea-auth/src/certs.rs
@@ -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,
+}
diff --git a/azalea-auth/src/lib.rs b/azalea-auth/src/lib.rs
index 794332d4..cf0d0401 100755
--- a/azalea-auth/src/lib.rs
+++ b/azalea-auth/src/lib.rs
@@ -2,6 +2,7 @@
mod auth;
mod cache;
+pub mod certs;
pub mod game_profile;
pub mod sessionserver;