Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
IPTV: HLS - add AES-128 decryption support
  • Loading branch information
perexg committed Nov 20, 2015
1 parent 0e5c899 commit c9ceae3
Showing 1 changed file with 167 additions and 34 deletions.
201 changes: 167 additions & 34 deletions src/input/mpegts/iptv/iptv_http.c
Expand Up @@ -2,6 +2,7 @@
* IPTV - HTTP/HTTPS handler
*
* Copyright (C) 2013 Adam Sutton
* Copyright (C) 2014,2015 Jaroslav Kysela
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
Expand All @@ -21,23 +22,43 @@
#include "iptv_private.h"
#include "http.h"
#include "misc/m3u.h"
#include "openssl/aes.h"

#if AES_BLOCK_SIZE != 16
#error "Wrong openssl!"
#endif

typedef struct http_priv {
iptv_mux_t *im;
http_client_t *hc;
uint8_t started;
sbuf_t m3u_sbuf;
sbuf_t key_sbuf;
int m3u_header;
uint64_t off;
char *host_url;
int64_t hls_seq;
char *hls_url;
uint8_t hls_url2;
uint8_t hls_encrypted;
char *hls_url_after_key;
char *hls_key_url;
htsmsg_t *hls_m3u;
htsmsg_t *hls_key;
uint8_t *hls_si;
time_t hls_last_si;
struct {
char tmp[AES_BLOCK_SIZE];
int tmp_len;
AES_KEY key;
unsigned char iv[AES_BLOCK_SIZE];
} hls_aes128;
} http_priv_t;

/***/

static int iptv_http_complete_key ( http_client_t *hc );

/*
*
*/
Expand Down Expand Up @@ -72,7 +93,7 @@ iptv_http_safe_global_lock( iptv_mux_t *im )
static char *
iptv_http_get_url( http_priv_t *hp, htsmsg_t *m )
{
htsmsg_t *items, *item, *inf, *sel = NULL;
htsmsg_t *items, *item, *inf, *sel = NULL, *key;
htsmsg_field_t *f;
int64_t seq, bandwidth, sel_bandwidth = 0;
int width, height, sel_width = 0, sel_height = 0;
Expand Down Expand Up @@ -129,11 +150,18 @@ iptv_http_get_url( http_priv_t *hp, htsmsg_t *m )
} else {
s = htsmsg_get_str(item, "m3u-url");
if (s && s[0]) {
key = htsmsg_get_map(item, "x-key");
if (key && strcmp(htsmsg_get_str(key, "METHOD") ?: "NONE", "NONE") != 0)
key = htsmsg_copy(key);
else
key = NULL;
s = strdup(s);
htsmsg_field_destroy(items, f);
if (hp->hls_url) {
hp->hls_url2 = 1;
hp->hls_m3u = m;
htsmsg_destroy(hp->hls_key);
hp->hls_key = key;
hp->hls_seq++;
}
return (char *)s;
Expand All @@ -157,6 +185,18 @@ iptv_http_get_url( http_priv_t *hp, htsmsg_t *m )
return NULL;
}

/*
*
*/
static void
iptv_http_data_aes128 ( http_priv_t *hp, sbuf_t *sb, int off )
{
unsigned char *in = sb->sb_data + off;
unsigned char *out = in;
size_t size = sb->sb_ptr - off;

AES_cbc_encrypt(in, out, size, &hp->hls_aes128.key, hp->hls_aes128.iv, AES_DECRYPT);
}

/*
* Connected
Expand Down Expand Up @@ -194,6 +234,8 @@ iptv_http_header ( http_client_t *hc )
}
hp->m3u_header++;
sbuf_reset(&hp->m3u_sbuf, 8192);
sbuf_reset(&hp->key_sbuf, 32);
hp->hls_encrypted = 0;
return 0;
}
}
Expand Down Expand Up @@ -221,7 +263,8 @@ iptv_http_data
{
http_priv_t *hp = hc->hc_aux;
iptv_mux_t *im;
int pause = 0, rem;
int pause = 0, off, rem;
uint8_t tsbuf[188];

if (hp == NULL || hp->im == NULL || hc->hc_code != HTTP_STATUS_OK)
return 0;
Expand All @@ -232,41 +275,62 @@ iptv_http_data
sbuf_append(&hp->m3u_sbuf, buf, len);
return 0;
}
if (hc->hc_data_complete == iptv_http_complete_key) {
sbuf_append(&hp->key_sbuf, buf, len);
return 0;
}

pthread_mutex_lock(&iptv_lock);

if (hp->off == 0) {
tvhtrace("iptv", "hexdump");
tvhlog_hexdump("iptv", buf, MAX(len, 4096));
tvhtrace("iptv", "hexdump end");
if (hp->hls_encrypted) {
off = im->mm_iptv_buffer.sb_ptr;
if (hp->hls_aes128.tmp_len + len >= AES_BLOCK_SIZE) {
if (hp->hls_aes128.tmp_len) {
sbuf_append(&im->mm_iptv_buffer, hp->hls_aes128.tmp, hp->hls_aes128.tmp_len);
rem = AES_BLOCK_SIZE - hp->hls_aes128.tmp_len;
hp->hls_aes128.tmp_len = 0;
sbuf_append(&im->mm_iptv_buffer, buf, rem);
len -= rem;
buf += rem;
}
rem = len % AES_BLOCK_SIZE;
sbuf_append(&im->mm_iptv_buffer, buf, len - rem);
buf += len - rem;
len = rem;
}
memcpy(hp->hls_aes128.tmp + hp->hls_aes128.tmp_len, buf, len);
hp->hls_aes128.tmp_len += len;
if (off == im->mm_iptv_buffer.sb_ptr)
return 0;
buf = im->mm_iptv_buffer.sb_data + im->mm_iptv_buffer.sb_ptr;
len = im->mm_iptv_buffer.sb_ptr - off;
assert((len % 16) == 0);
iptv_http_data_aes128(hp, &im->mm_iptv_buffer, off);
} else {
sbuf_append(&im->mm_iptv_buffer, buf, len);
}
tsdebug_write((mpegts_mux_t *)im, buf, len);
hp->off += len;

if (hp->hls_url && hp->off < 2*188 && len >= 2*188) {
if (hp->hls_url && hp->off == 0 && len >= 2*188) {
free(hp->hls_si);
hp->hls_si = malloc(2*188);
memcpy(hp->hls_si, buf, 2*188);
}

pthread_mutex_lock(&iptv_lock);

if (dispatch_clock != hp->hls_last_si && hp->hls_si) {
/* do rounding to next MPEG-TS packet */
/* do rounding to start of the last MPEG-TS packet */
rem = 188 - (hp->off % 188);
if (rem < 188) {
if (len < rem)
goto next;
sbuf_append(&im->mm_iptv_buffer, buf, rem);
buf += rem;
len -= rem;
if (im->mm_iptv_buffer.sb_ptr >= rem) {
im->mm_iptv_buffer.sb_ptr -= rem;
memcpy(tsbuf, im->mm_iptv_buffer.sb_data + im->mm_iptv_buffer.sb_ptr, rem);
sbuf_append(&im->mm_iptv_buffer, hp->hls_si, 2*188);
hp->hls_last_si = dispatch_clock;
sbuf_append(&im->mm_iptv_buffer, tsbuf, rem);
hp->off += rem;
}
sbuf_append(&im->mm_iptv_buffer, hp->hls_si, 2*188);
hp->hls_last_si = dispatch_clock;
}

next:
hp->off += len;
sbuf_append(&im->mm_iptv_buffer, buf, len);
tsdebug_write((mpegts_mux_t *)im, buf, len);

if (len > 0)
if (iptv_input_recv_packets(im, len) == 1)
pause = hc->hc_pause = 1;
Expand All @@ -284,14 +348,32 @@ iptv_http_data
/*
* Complete data
*/
static void
iptv_http_reconnect ( http_client_t *hc, const char *url )
{
url_t u;
int r;

urlinit(&u);
if (!urlparse(url, &u)) {
hc->hc_keepalive = 0;
r = http_client_simple_reconnect(hc, &u, HTTP_VERSION_1_1);
if (r < 0)
tvherror("iptv", "cannot reopen http client: %d'", r);
} else {
tvherror("iptv", "m3u url invalid '%s'", url);
}
urlreset(&u);
}

static int
iptv_http_complete
( http_client_t *hc )
{
http_priv_t *hp = hc->hc_aux;
const char *s;
char *url;
htsmsg_t *m, *m2;
url_t u;
int r;

if (hp == NULL || hp->im == NULL)
Expand All @@ -312,23 +394,46 @@ iptv_http_complete
url = iptv_http_get_url(hp, m);
if (hp->hls_m3u == m)
m = NULL;
if (hp->hls_key) {
s = htsmsg_get_str(hp->hls_key, "METHOD");
if (s == NULL || strcmp(s, "AES-128")) {
tvherror("iptv", "unknown crypto method '%s'", s);
goto end;
}
memset(&hp->hls_aes128.iv, 0, sizeof(hp->hls_aes128.iv));
s = htsmsg_get_str(hp->hls_key, "IV");
if (s != NULL) {
if (s[0] != '0' || s[1] != 'x' || strlen(s) != (AES_BLOCK_SIZE * 2) + 2) {
tvherror("iptv", "unknown IV type or length (%s)", s);
goto end;
}
hex2bin(hp->hls_aes128.iv, sizeof(hp->hls_aes128.iv), s + 2);
}
s = htsmsg_get_str(hp->hls_key, "URI");
if (s == NULL) {
tvherror("iptv", "no URI in KEY attribute");
goto end;
}
hp->hls_encrypted = 1;
if (strcmp(hp->hls_key_url ?: "", s)) {
free(hp->hls_key_url);
hp->hls_key_url = strdup(s);
free(hp->hls_url_after_key);
hp->hls_url_after_key = url;
url = strdup(s);
hc->hc_data_complete = iptv_http_complete_key;
sbuf_reset(&hp->key_sbuf, 32);
}
}
tvhtrace("iptv", "m3u url: '%s'", url);
if (url == NULL) {
tvherror("iptv", "m3u contents parsing failed");
goto fin;
}
new_m3u:
urlinit(&u);
if (!urlparse(url, &u)) {
hc->hc_keepalive = 0;
r = http_client_simple_reconnect(hc, &u, HTTP_VERSION_1_1);
if (r < 0)
tvherror("iptv", "cannot reopen http client: %d'", r);
} else {
tvherror("iptv", "m3u url invalid '%s'", url);
}
iptv_http_reconnect(hc, url);
end:
free(url);
urlreset(&u);
fin:
htsmsg_destroy(m);
} else {
Expand All @@ -352,6 +457,29 @@ iptv_http_complete
return 0;
}

static int
iptv_http_complete_key
( http_client_t *hc )
{
http_priv_t *hp = hc->hc_aux;

if (hp == NULL)
return 0;
tvhtrace("iptv", "received key len %d", hp->key_sbuf.sb_ptr);
if (hp->key_sbuf.sb_ptr != AES_BLOCK_SIZE) {
tvherror("iptv", "AES-128 key wrong length (%d)\n", hp->key_sbuf.sb_ptr);
return 0;
}
AES_set_decrypt_key(hp->key_sbuf.sb_data, 128, &hp->hls_aes128.key);
hc->hc_data_complete = iptv_http_complete;
iptv_http_reconnect(hc, hp->hls_url_after_key);
free(hp->hls_url_after_key);
hp->hls_url_after_key = NULL;
htsmsg_destroy(hp->hls_key);
hp->hls_key = NULL;
return 0;
}

/*
* Custom headers
*/
Expand Down Expand Up @@ -392,6 +520,7 @@ iptv_http_start
hp->hc = hc;
im->im_data = hp;
sbuf_init(&hp->m3u_sbuf);
sbuf_init(&hp->key_sbuf);
sbuf_init_fixed(&im->mm_iptv_buffer, IPTV_BUF_SIZE);
http_client_register(hc); /* register to the HTTP thread */
r = http_client_simple(hc, u);
Expand Down Expand Up @@ -420,8 +549,12 @@ iptv_http_stop
pthread_mutex_lock(&iptv_lock);
im->im_data = NULL;
sbuf_free(&hp->m3u_sbuf);
sbuf_free(&hp->key_sbuf);
htsmsg_destroy(hp->hls_m3u);
htsmsg_destroy(hp->hls_key);
free(hp->hls_url);
free(hp->hls_url_after_key);
free(hp->hls_key_url);
free(hp->hls_si);
free(hp->host_url);
free(hp);
Expand Down

0 comments on commit c9ceae3

Please sign in to comment.