diff --git a/record-daemon/Cargo.lock b/record-daemon/Cargo.lock index e5a2dd0..f75b4de 100644 --- a/record-daemon/Cargo.lock +++ b/record-daemon/Cargo.lock @@ -656,9 +656,9 @@ dependencies = [ "futures-util", "http 0.2.12", "hyper", - "rustls", + "rustls 0.21.12", "tokio", - "tokio-rustls", + "tokio-rustls 0.24.1", ] [[package]] @@ -1087,6 +1087,8 @@ dependencies = [ "parking_lot", "regex", "reqwest", + "rustls 0.22.4", + "rustls-pki-types", "serde", "serde_json", "signal-hook", @@ -1175,7 +1177,7 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls", + "rustls 0.21.12", "rustls-pemfile", "serde", "serde_json", @@ -1183,13 +1185,13 @@ dependencies = [ "sync_wrapper", "system-configuration", "tokio", - "tokio-rustls", + "tokio-rustls 0.24.1", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "webpki-roots", + "webpki-roots 0.25.4", "winreg", ] @@ -1228,10 +1230,24 @@ checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" dependencies = [ "log", "ring", - "rustls-webpki", + "rustls-webpki 0.101.7", "sct", ] +[[package]] +name = "rustls" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" +dependencies = [ + "log", + "ring", + "rustls-pki-types", + "rustls-webpki 0.102.8", + "subtle", + "zeroize", +] + [[package]] name = "rustls-pemfile" version = "1.0.4" @@ -1241,6 +1257,15 @@ dependencies = [ "base64 0.21.7", ] +[[package]] +name = "rustls-pki-types" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" +dependencies = [ + "zeroize", +] + [[package]] name = "rustls-webpki" version = "0.101.7" @@ -1251,6 +1276,17 @@ dependencies = [ "untrusted", ] +[[package]] +name = "rustls-webpki" +version = "0.102.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + [[package]] name = "rustversion" version = "1.0.22" @@ -1451,6 +1487,12 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + [[package]] name = "syn" version = "2.0.117" @@ -1586,7 +1628,18 @@ version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" dependencies = [ - "rustls", + "rustls 0.21.12", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" +dependencies = [ + "rustls 0.22.4", + "rustls-pki-types", "tokio", ] @@ -1620,8 +1673,12 @@ checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38" dependencies = [ "futures-util", "log", + "rustls 0.22.4", + "rustls-pki-types", "tokio", + "tokio-rustls 0.25.0", "tungstenite", + "webpki-roots 0.26.11", ] [[package]] @@ -1764,6 +1821,8 @@ dependencies = [ "httparse", "log", "rand", + "rustls 0.22.4", + "rustls-pki-types", "sha1", "thiserror", "url", @@ -1990,6 +2049,24 @@ version = "0.25.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" +[[package]] +name = "webpki-roots" +version = "0.26.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" +dependencies = [ + "webpki-roots 1.0.6", +] + +[[package]] +name = "webpki-roots" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cfaf3c063993ff62e73cb4311efde4db1efb31ab78a3e5c457939ad5cc0bed" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "windows-core" version = "0.62.2" @@ -2374,6 +2451,12 @@ dependencies = [ "synstructure", ] +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" + [[package]] name = "zerotrie" version = "0.2.3" diff --git a/record-daemon/Cargo.toml b/record-daemon/Cargo.toml index 62646f1..f92b3ad 100644 --- a/record-daemon/Cargo.toml +++ b/record-daemon/Cargo.toml @@ -15,8 +15,10 @@ serde = { version = "1", features = ["derive"] } serde_json = "1" toml = "0.8" -# WebSocket for LQP -tokio-tungstenite = "0.21" +# WebSocket for LQP (with TLS support) +tokio-tungstenite = { version = "0.21", features = ["rustls-tls-webpki-roots"] } +rustls = "0.22" +rustls-pki-types = "1" # HTTP client for LQP REST API reqwest = { version = "0.11", default-features = false, features = ["json", "rustls-tls"] } diff --git a/record-daemon/src/lqp/client.rs b/record-daemon/src/lqp/client.rs index 1d713c1..3b114b0 100644 --- a/record-daemon/src/lqp/client.rs +++ b/record-daemon/src/lqp/client.rs @@ -6,13 +6,65 @@ use std::sync::Arc; use futures::{SinkExt, StreamExt}; use tokio::sync::{broadcast, RwLock}; -use tokio_tungstenite::{connect_async_with_config, tungstenite::protocol::Message}; +use tokio_tungstenite::{connect_async_tls_with_config, tungstenite::protocol::Message}; use tracing::{debug, error, info, trace, warn}; use super::auth::LockfileCredentials; use super::events::GameEvent; use crate::error::{LqpError, Result}; +/// Custom certificate verifier that accepts any certificate. +/// This is needed because the League Client uses a self-signed certificate. +#[derive(Debug)] +struct InsecureVerifier; + +impl rustls::client::danger::ServerCertVerifier for InsecureVerifier { + fn verify_server_cert( + &self, + _end_entity: &rustls::pki_types::CertificateDer<'_>, + _intermediates: &[rustls::pki_types::CertificateDer<'_>], + _server_name: &rustls::pki_types::ServerName<'_>, + _ocsp_response: &[u8], + _now: rustls::pki_types::UnixTime, + ) -> std::result::Result { + // Accept any certificate - League Client uses self-signed certificates + Ok(rustls::client::danger::ServerCertVerified::assertion()) + } + + fn verify_tls12_signature( + &self, + _message: &[u8], + _cert: &rustls::pki_types::CertificateDer<'_>, + _dss: &rustls::DigitallySignedStruct, + ) -> std::result::Result { + Ok(rustls::client::danger::HandshakeSignatureValid::assertion()) + } + + fn verify_tls13_signature( + &self, + _message: &[u8], + _cert: &rustls::pki_types::CertificateDer<'_>, + _dss: &rustls::DigitallySignedStruct, + ) -> std::result::Result { + Ok(rustls::client::danger::HandshakeSignatureValid::assertion()) + } + + fn supported_verify_schemes(&self) -> Vec { + vec![ + rustls::SignatureScheme::RSA_PKCS1_SHA256, + rustls::SignatureScheme::RSA_PKCS1_SHA384, + rustls::SignatureScheme::RSA_PKCS1_SHA512, + rustls::SignatureScheme::ECDSA_NISTP256_SHA256, + rustls::SignatureScheme::ECDSA_NISTP384_SHA384, + rustls::SignatureScheme::ECDSA_NISTP521_SHA512, + rustls::SignatureScheme::RSA_PSS_SHA256, + rustls::SignatureScheme::RSA_PSS_SHA384, + rustls::SignatureScheme::RSA_PSS_SHA512, + rustls::SignatureScheme::ED25519, + ] + } +} + /// LQP WebSocket endpoints to subscribe to. const SUBSCRIBE_ENDPOINTS: &[&str] = &[ "/lol-gameflow/v1/gameflow-phase", @@ -186,16 +238,34 @@ impl LqpClient { info!("Connecting to LQP WebSocket at {}", ws_url); + // Create a TLS connector that accepts the self-signed certificate from League Client + use tokio_tungstenite::Connector; + use tokio_tungstenite::tungstenite::http::HeaderValue; + + let config = rustls::ClientConfig::builder() + .dangerous() + .with_custom_certificate_verifier(Arc::new(InsecureVerifier)) + .with_no_client_auth(); + + let connector = Connector::Rustls(Arc::new(config)); + // Build WebSocket request with auth header let request = tokio_tungstenite::tungstenite::http::Request::builder() .uri(&ws_url) .header("Authorization", auth_header) + .header("Host", format!("127.0.0.1:{}", creds.port)) + .header("Connection", "Upgrade") + .header("Upgrade", "websocket") + .header("Sec-WebSocket-Version", "13") + .header("Sec-WebSocket-Key", tokio_tungstenite::tungstenite::handshake::client::generate_key()) .body(()) .map_err(|e| LqpError::ConnectionFailed(e.to_string()))?; - let (ws_stream, _) = connect_async_with_config( - request, None, false, - // None, + let (ws_stream, _) = connect_async_tls_with_config( + request, + None, + false, + Some(connector), ) .await .map_err(|e| LqpError::WebSocketError(e.to_string()))?;