random encrypt secret generation

hls redirect fix
This commit is contained in:
euzu
2025-04-16 16:50:33 +02:00
parent 60c0766a87
commit 2738ca6755
5 changed files with 20 additions and 15 deletions
+2 -1
View File
@@ -43,6 +43,7 @@ pub(in crate::api) async fn handle_hls_stream_request(app_state: &Arc<AppState>,
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(&params.token) else { return axum::http::StatusCode::BAD_REQUEST.into_response(); };
let Ok(hls_url) = crypto_utils::decrypt_text(&app_state.config.t_encrypt_secret, &params.token) else { return axum::http::StatusCode::BAD_REQUEST.into_response(); };
let target_name = &target.name;
let virtual_id = params.stream_id;
+2 -2
View File
@@ -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();
}
+3
View File
@@ -1558,6 +1558,8 @@ pub struct Config {
pub t_provider_connections_exhausted_video: Option<Arc<Vec<u8>>>,
#[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();
+5 -2
View File
@@ -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));
}
+8 -10
View File
@@ -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<String, M3uFilterError> {
pub fn encrypt_text(secret: &[u8;16], text: &str) -> Result<String, M3uFilterError> {
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<String, M3uFilterError> {
Ok(general_purpose::URL_SAFE_NO_PAD.encode(out))
}
pub fn decrypt_text(encrypted_text: &str) -> Result<String, M3uFilterError> {
pub fn decrypt_text(secret: &[u8;16], encrypted_text: &str) -> Result<String, M3uFilterError> {
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<String, M3uFilterError> {
#[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);
}