diff --git a/src/api/endpoints/hls_api.rs b/src/api/endpoints/hls_api.rs index e18fb0bfc..d9ae7bfd4 100644 --- a/src/api/endpoints/hls_api.rs +++ b/src/api/endpoints/hls_api.rs @@ -43,6 +43,7 @@ pub(in crate::api) async fn handle_hls_stream_request(app_state: &Arc, match request::download_text_content(Arc::clone(&app_state.http_client), input, &url, None).await { Ok((content, response_url)) => { let rewrite_hls_props = RewriteHlsProps { + secret: &app_state.config.t_encrypt_secret, base_url: &server_info.get_base_url(), content: &content, hls_url: response_url, @@ -74,7 +75,7 @@ async fn hls_api_stream( return create_custom_video_stream_response(&app_state.config, &CustomVideoStreamType::UserConnectionsExhausted).into_response(); } - let Ok(hls_url) = crypto_utils::decrypt_text(¶ms.token) else { return axum::http::StatusCode::BAD_REQUEST.into_response(); }; + let Ok(hls_url) = crypto_utils::decrypt_text(&app_state.config.t_encrypt_secret, ¶ms.token) else { return axum::http::StatusCode::BAD_REQUEST.into_response(); }; let target_name = &target.name; let virtual_id = params.stream_id; diff --git a/src/api/endpoints/xtream_api.rs b/src/api/endpoints/xtream_api.rs index bbee90891..5889ba602 100644 --- a/src/api/endpoints/xtream_api.rs +++ b/src/api/endpoints/xtream_api.rs @@ -223,8 +223,8 @@ async fn xtream_player_api_stream( // debug_if_enabled!("Redirecting stream request to {}", sanitize_sensitive_info(redirect_url)); // return redirect(redirect_url).into_response(); // } - if pli.item_type == PlaylistItemType::LiveDash { - let redirect_url = &replace_url_extension(&pli.url, DASH_EXT); + if is_hls_request || pli.item_type == PlaylistItemType::LiveDash { + let redirect_url = if is_hls_request { &replace_url_extension(&pli.url, HLS_EXT) } else { &replace_url_extension(&pli.url, DASH_EXT) }; debug_if_enabled!("Redirecting stream request to {}", sanitize_sensitive_info(redirect_url)); return redirect(redirect_url).into_response(); } diff --git a/src/model/config.rs b/src/model/config.rs index c07e06b8b..3e32cbd33 100644 --- a/src/model/config.rs +++ b/src/model/config.rs @@ -1558,6 +1558,8 @@ pub struct Config { pub t_provider_connections_exhausted_video: Option>>, #[serde(skip)] pub t_access_token_secret: [u8;32], + #[serde(skip)] + pub t_encrypt_secret: [u8;16], } impl Config { @@ -1796,6 +1798,7 @@ impl Config { pub fn prepare(&mut self) -> Result<(), M3uFilterError> { self.t_access_token_secret = generate_secret(); + self.t_encrypt_secret = <&[u8] as TryInto<[u8;16]>>::try_into(&generate_secret()[0..16]).map_err(|err| M3uFilterError::new(M3uFilterErrorKind::Info, err.to_string()))?; let work_dir = &self.working_dir; self.working_dir = file_utils::get_working_path(work_dir); self.prepare_custom_stream_response(); diff --git a/src/processing/parser/hls.rs b/src/processing/parser/hls.rs index b5e9034b5..71235e0d1 100644 --- a/src/processing/parser/hls.rs +++ b/src/processing/parser/hls.rs @@ -6,6 +6,7 @@ pub const HLS_PREFIX: &str = "hls"; pub struct RewriteHlsProps<'a> { + pub secret: &'a [u8;16], pub base_url: &'a str, pub content: &'a str, pub hls_url: String, @@ -30,6 +31,8 @@ fn rewrite_hls_url(input: &str, replacement: &str) -> String { } } +// TODO # line can have URI parts whcih shuld rewritten too + pub fn rewrite_hls(user: &ProxyUserCredentials, props: &RewriteHlsProps) -> String { let username = &user.username; let password = &user.password; @@ -38,9 +41,9 @@ pub fn rewrite_hls(user: &ProxyUserCredentials, props: &RewriteHlsProps) -> Stri if line.starts_with('#') { result.push(line.to_string()); } else if let Ok(token) = if line.starts_with("http") { - encrypt_text(line) + encrypt_text(props.secret, line) } else { - encrypt_text(&rewrite_hls_url(&props.hls_url, line)) + encrypt_text(props.secret, &rewrite_hls_url(&props.hls_url, line)) } { result.push(format!("{}/{HLS_PREFIX}/{username}/{password}/{}/{}/{token}", props.base_url, props.input_id, props.virtual_id)); } diff --git a/src/utils/crypto_utils.rs b/src/utils/crypto_utils.rs index 3393cbd9a..700d0f0e0 100644 --- a/src/utils/crypto_utils.rs +++ b/src/utils/crypto_utils.rs @@ -3,13 +3,10 @@ use base64::{engine::general_purpose, Engine as _}; use rand::Rng; use crate::m3u_filter_error::{M3uFilterError, M3uFilterErrorKind}; -const SECRET_KEY: &[u8; 16] = b"my-secret-32-byt"; // 32 bytes = AES-256 - -pub fn encrypt_text(text: &str) -> Result { +pub fn encrypt_text(secret: &[u8;16], text: &str) -> Result { let iv: [u8; 16] = rand::rng().random(); // Random IV (AES-CBC 16 Bytes) let cipher = Cipher::aes_128_cbc(); - - let mut crypter = Crypter::new(cipher, Mode::Encrypt, SECRET_KEY, Some(&iv)).map_err(|err| M3uFilterError::new(M3uFilterErrorKind::Info, err.to_string()))?; + let mut crypter = Crypter::new(cipher, Mode::Encrypt, secret, Some(&iv)).map_err(|err| M3uFilterError::new(M3uFilterErrorKind::Info, err.to_string()))?; let mut ciphertext = vec![0; text.len() + cipher.block_size()]; let mut count = crypter.update(text.as_bytes(), &mut ciphertext).map_err(|err| M3uFilterError::new(M3uFilterErrorKind::Info, err.to_string()))?; count += crypter.finalize(&mut ciphertext[count..]).map_err(|err| M3uFilterError::new(M3uFilterErrorKind::Info, err.to_string()))?; @@ -21,12 +18,11 @@ pub fn encrypt_text(text: &str) -> Result { Ok(general_purpose::URL_SAFE_NO_PAD.encode(out)) } -pub fn decrypt_text(encrypted_text: &str) -> Result { +pub fn decrypt_text(secret: &[u8;16], encrypted_text: &str) -> Result { let data = general_purpose::URL_SAFE_NO_PAD.decode(encrypted_text).map_err(|err| M3uFilterError::new(M3uFilterErrorKind::Info, err.to_string()))?; let (iv, ciphertext) = data.split_at(16); // first 16 bytes IV let cipher = Cipher::aes_128_cbc(); - - let mut crypter = Crypter::new(cipher, Mode::Decrypt, SECRET_KEY, Some(iv)).map_err(|err| M3uFilterError::new(M3uFilterErrorKind::Info, err.to_string()))?; + let mut crypter = Crypter::new(cipher, Mode::Decrypt, secret, Some(iv)).map_err(|err| M3uFilterError::new(M3uFilterErrorKind::Info, err.to_string()))?; let mut decrypted = vec![0; ciphertext.len() + cipher.block_size()]; let mut count = crypter.update(ciphertext, &mut decrypted).map_err(|err| M3uFilterError::new(M3uFilterErrorKind::Info, err.to_string()))?; count += crypter.finalize(&mut decrypted[count..]).map_err(|err| M3uFilterError::new(M3uFilterErrorKind::Info, err.to_string()))?; @@ -37,13 +33,15 @@ pub fn decrypt_text(encrypted_text: &str) -> Result { #[cfg(test)] mod tests { + use rand::Rng; use crate::utils::crypto_utils::{decrypt_text, encrypt_text}; #[test] fn test_encrypt() { + let secret: [u8; 16] = rand::rng().random(); // Random IV (AES-CBC 16 Bytes) let plain = "hello world"; - let encrypted = encrypt_text(&plain); - let decrypted = decrypt_text(&encrypted.unwrap()).unwrap(); + let encrypted = encrypt_text(&secret, &plain); + let decrypted = decrypt_text(&secret, &encrypted.unwrap()).unwrap(); assert_eq!(decrypted, plain); }