Browse Source

Add detection of encrypted output and disable encrypted output by default

With this commit if tsdecrypt detects that the stream is still
encrypted after passing it through decryption it doesn't output it.

You can enable the output of encrypted streams using --output-enc-pass
option.
Georgi Chorbadzhiyski 6 years ago
parent
commit
ec04fb64e7
8 changed files with 196 additions and 2 deletions
  1. 4
    0
      ChangeLog
  2. 1
    0
      README
  3. 102
    0
      bitstream.h
  4. 6
    0
      data.c
  5. 5
    0
      data.h
  6. 62
    0
      process.c
  7. 9
    0
      tsdecrypt.1
  8. 7
    2
      tsdecrypt.c

+ 4
- 0
ChangeLog View File

@@ -6,6 +6,10 @@ xxxx-xx-xx : Version -next
6 6
    races if the scripts are used for logging.
7 7
  * Start passing _INPUT_ADDR and _OUTPUT_ADDR variables to notify script.
8 8
  * Add --status-file (-0) option. This file keeps latest program status.
9
+ * Add two new notification messages - OUTPUT_ENCRYPTED and OUTPUT_OK
10
+ * Do not output the stream if it can not be decrypted. You can still
11
+   enable the output (ignorring encrypt detection) by using
12
+   --output-enc-pass option.
9 13
 
10 14
 2013-09-12 : Version 10.0
11 15
  * Add --ecm-only (-v) option. This allows processing of ECMs but without

+ 1
- 0
README View File

@@ -134,6 +134,7 @@ Output options:
134 134
  -g --output-tos <tos>      | Set TOS value of output packets. Default: none
135 135
  -u --no-output-on-error    | Do not output data when the code word is missing.
136 136
  -p --no-output-filter      | Disable output filtering. Default: enabled
137
+ -3 --output-enc-pass       | Output the stream even if can not be decrypted.
137 138
  -y --output-nit-pass       | Pass through NIT.
138 139
  -w --output-eit-pass       | Pass through EIT (EPG).
139 140
  -x --output-tdt-pass       | Pass through TDT/TOT.

+ 102
- 0
bitstream.h View File

@@ -0,0 +1,102 @@
1
+/*
2
+ * TS bitstream functions
3
+ * The following functions are copied from bitstream/mpeg.ts and bitstream/mpeg/pes.h
4
+
5
+ * Copyright (c) 2010-2011 VideoLAN
6
+
7
+ * Permission is hereby granted, free of charge, to any person obtaining
8
+ * a copy of this software and associated documentation files (the
9
+ * "Software"), to deal in the Software without restriction, including
10
+ * without limitation the rights to use, copy, modify, merge, publish,
11
+ * distribute, sublicense, and/or sell copies of the Software, and to
12
+ * permit persons to whom the Software is furnished to do so, subject
13
+ * to the following conditions:
14
+
15
+ * The above copyright notice and this permission notice shall be
16
+ * included in all copies or substantial portions of the Software.
17
+
18
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
21
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
22
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25
+ */
26
+#ifndef BITSTREAM_H
27
+#define BITSTREAM_H
28
+
29
+#include <stdbool.h>
30
+#include <inttypes.h>
31
+
32
+#define TS_SIZE                     188
33
+#define TS_HEADER_SIZE              4
34
+#define PES_HEADER_SIZE_PTS         14
35
+#define PES_HEADER_SIZE_PTSDTS      19
36
+#define PES_STREAM_ID_MIN           0xbc
37
+#define PES_STREAM_ID_PRIVATE_2     0xbf
38
+
39
+static inline bool ts_validate(const uint8_t *p_ts)
40
+{
41
+    return p_ts[0] == 0x47;
42
+}
43
+
44
+static inline bool ts_get_unitstart(const uint8_t *p_ts)
45
+{
46
+    return !!(p_ts[1] & 0x40);
47
+}
48
+
49
+static inline bool ts_has_payload(const uint8_t *p_ts)
50
+{
51
+    return !!(p_ts[3] & 0x10);
52
+}
53
+
54
+static inline bool ts_has_adaptation(const uint8_t *p_ts)
55
+{
56
+    return !!(p_ts[3] & 0x20);
57
+}
58
+
59
+static inline uint8_t ts_get_adaptation(const uint8_t *p_ts)
60
+{
61
+    return p_ts[4];
62
+}
63
+
64
+static inline uint8_t pes_get_streamid(const uint8_t *p_pes)
65
+{
66
+    return p_pes[3];
67
+}
68
+
69
+static inline bool pes_validate(const uint8_t *p_pes)
70
+{
71
+    return (p_pes[0] == 0x0 && p_pes[1] == 0x0 && p_pes[2] == 0x1
72
+             && p_pes[3] >= PES_STREAM_ID_MIN);
73
+}
74
+
75
+static inline bool pes_validate_header(const uint8_t *p_pes)
76
+{
77
+    return ((p_pes[6] & 0xc0) == 0x80);
78
+}
79
+
80
+static inline bool pes_has_pts(const uint8_t *p_pes)
81
+{
82
+    return !!(p_pes[7] & 0x80);
83
+}
84
+
85
+static inline bool pes_has_dts(const uint8_t *p_pes)
86
+{
87
+    return (p_pes[7] & 0xc0) == 0xc0;
88
+}
89
+
90
+static inline bool pes_validate_pts(const uint8_t *p_pes)
91
+{
92
+    return ((p_pes[9] & 0xe1) == 0x21)
93
+            && (p_pes[11] & 0x1) && (p_pes[13] & 0x1);
94
+}
95
+
96
+static inline bool pes_validate_dts(const uint8_t *p_pes)
97
+{
98
+    return (p_pes[9] & 0x10) && ((p_pes[14] & 0xf1) == 0x11)
99
+            && (p_pes[16] & 0x1) && (p_pes[18] & 0x1);
100
+}
101
+
102
+#endif

+ 6
- 0
data.c View File

@@ -19,6 +19,7 @@
19 19
 #include "data.h"
20 20
 #include "csa.h"
21 21
 #include "camd.h"
22
+#include "util.h"
22 23
 
23 24
 void data_init(struct ts *ts) {
24 25
 	memset(ts, 0, sizeof(struct ts));
@@ -99,6 +100,11 @@ void data_init(struct ts *ts) {
99 100
 	ts->cw_last_warn= ts->cw_last_warn + ts->cw_warn_sec;
100 101
 	ts->key.ts      = time(NULL);
101 102
 
103
+	ts->allow_encrypted_output   = 0;
104
+	ts->output_is_encrypted      = 0;
105
+	ts->last_encrypted_output_ts = get_time();
106
+	ts->last_decrypted_output_ts = get_time();
107
+
102 108
 	ts->input.fd    = 0; // STDIN
103 109
 	ts->input.type  = FILE_IO;
104 110
 

+ 5
- 0
data.h View File

@@ -291,6 +291,11 @@ struct ts {
291 291
 	time_t				last_not_scrambled_packet_ts;
292 292
 	time_t				last_not_scrambled_report_ts;
293 293
 
294
+	bool				allow_encrypted_output;
295
+	bool				output_is_encrypted;
296
+	int64_t				last_encrypted_output_ts;
297
+	int64_t				last_decrypted_output_ts;
298
+
294 299
 	unsigned int		pid_report;
295 300
 	unsigned int		pid_stats[MAX_PIDS];
296 301
 

+ 62
- 0
process.c View File

@@ -16,6 +16,7 @@
16 16
 #include <string.h>
17 17
 #include <sys/uio.h>
18 18
 
19
+#include "bitstream.h"
19 20
 #include "data.h"
20 21
 #include "csa.h"
21 22
 #include "tables.h"
@@ -259,6 +260,38 @@ void *decode_thread(void *_ts) {
259 260
 	return NULL;
260 261
 }
261 262
 
263
+/*
264
+	Return value:
265
+		ret        == 0    - No valid payload was found
266
+		ret & 0x01 == 0x01 - PES was found
267
+		ret & 0x02 == 0x02 - PTS was found
268
+		ret & 0x04 == 0x04 - DTS was found
269
+*/
270
+static unsigned int ts_have_valid_pes(uint8_t *buf, unsigned int buffer_size) {
271
+	unsigned int ret = 0;
272
+	uint8_t *buf_end = buf + buffer_size;
273
+	while (buf < buf_end && ts_validate(buf)) {
274
+		uint16_t header_size = TS_HEADER_SIZE + (ts_has_adaptation(buf) ? 1 : 0) + ts_get_adaptation(buf);
275
+		if (ts_get_unitstart(buf) && ts_has_payload(buf) && header_size + PES_HEADER_SIZE_PTS <= TS_SIZE) {
276
+			//printf("Got payload\n");
277
+			if (pes_validate(buf + header_size) && pes_get_streamid(buf + header_size) != PES_STREAM_ID_PRIVATE_2 && pes_validate_header(buf + header_size)) {
278
+				//printf("Got PES\n");
279
+				ret |= 0x01;
280
+				if (pes_has_pts(buf + header_size) && pes_validate_pts(buf + header_size)) {
281
+					ret |= 0x02;
282
+					//printf("Got PTS\n");
283
+					if (header_size + PES_HEADER_SIZE_PTSDTS <= TS_SIZE && pes_has_dts(buf + header_size) && pes_validate_dts(buf + header_size)) {
284
+						//printf("Got DTS\n");
285
+						ret |= 0x04;
286
+					}
287
+				}
288
+			}
289
+		}
290
+		buf += TS_SIZE;
291
+	}
292
+	return ret;
293
+}
294
+
262 295
 static inline void output_write(struct ts *ts, uint8_t *data, unsigned int data_size) {
263 296
 	if (!data)
264 297
 		return;
@@ -266,6 +299,35 @@ static inline void output_write(struct ts *ts, uint8_t *data, unsigned int data_
266 299
 		return;
267 300
 	if (ts->no_output_on_error && !ts->camd.key->is_valid_cw)
268 301
 		return;
302
+	if (!ts->allow_encrypted_output) {
303
+		int64_t now = get_time();
304
+		int ret;
305
+		if ((ret = ts_have_valid_pes(data, data_size)) == 0) { // Is the output encrypted?
306
+			/* The output is encrypted, check if 1000 ms have passed and if such, notify that we probably have invalid key */
307
+			ts->last_encrypted_output_ts = now;
308
+			if (now > ts->last_decrypted_output_ts + 500000) {
309
+				if (!ts->output_is_encrypted) {
310
+					ts->output_is_encrypted = 1;
311
+					ts_LOGf("OUT | *ERR* The output is encrypted for %" PRId64 " ms, stopping output\n", (now - ts->last_decrypted_output_ts) / 1000);
312
+					notify(ts, "ENCRYPTED_OUTPUT", "The output can not be decrypted");
313
+				}
314
+			}
315
+		} else {
316
+			ts->last_decrypted_output_ts = now;
317
+			if (ts->output_is_encrypted) {
318
+				ts_LOGf("OUT | Got decrypted data: %s %s %s\n",
319
+					(ret & 0x01) == 0x01 ? "PES" : "   ",
320
+					(ret & 0x02) == 0x02 ? "PTS" : "   ",
321
+					(ret & 0x04) == 0x04 ? "DTS" : "   "
322
+				);
323
+				notify(ts, "OUTPUT_OK", "The output is decrypted");
324
+			}
325
+			ts->output_is_encrypted = 0;
326
+		}
327
+		if (ts->output_is_encrypted)
328
+			return;
329
+	}
330
+
269 331
 	if (!ts->rtp_output) {
270 332
 		if (write(ts->output.fd, data, data_size) < 0) {
271 333
 			perror("write(output_fd)");

+ 9
- 0
tsdecrypt.1 View File

@@ -175,6 +175,11 @@ Disable output filtering. By default the output filter is enabled and only
175 175
 PAT/PMT/SDT and data packets are left in the output. Everything else not
176 176
 mentioned in PMT like NIT, EIT, TDT tables and unknown pids is removed.
177 177
 .TP
178
+\fB\-3\fR, \fB\-\-output\-enc\-pass\fR
179
+Output the stream even if it is encrypted. By default tsdecrypt detects
180
+if the stream is still encrypted after passing through decryption and
181
+does not output it.
182
+.TP
178 183
 \fB\-y\fR, \fB\-\-output\-nit\-pass\fR
179 184
 Pass through NIT packets when output filtering is enabled.
180 185
 .TP
@@ -393,6 +398,10 @@ currently defined events are:
393 398
 
394 399
   \fBINPUT_OK\fR        The data have appeared on the input.
395 400
 
401
+  \fBENCRYPTED_OUTPUT\fR The received keyword can not decrypt the stream
402
+
403
+  \fBOUTPUT_OK\fR       The stream that is beeing processed is decrypted
404
+
396 405
   \fBSTOP\fR            tsdecrypt was stopped.
397 406
 
398 407
 See \fBnotify\-script.example\fR for an example on how to create external

+ 7
- 2
tsdecrypt.c View File

@@ -77,9 +77,9 @@ static void LOG_func(const char *msg) {
77 77
 		LOG(msg);
78 78
 }
79 79
 
80
-static const char short_options[] = "i:d:N:90:Sl:L:F:I:1:RzM:T:W:O:o:t:rk:g:upwxyc:C:Y:Q:A:s:U:P:B:46eZ:Ef:a:X:vqH:G:2:KJ:D:jbhVn:m:";
80
+static const char short_options[] = "i:d:N:90:Sl:L:F:I:1:RzM:T:W:O:o:t:rk:g:upwx3yc:C:Y:Q:A:s:U:P:B:46eZ:Ef:a:X:vqH:G:2:KJ:D:jbhVn:m:";
81 81
 
82
-// Unused short options: 3578
82
+// Unused short options: 578
83 83
 static const struct option long_options[] = {
84 84
 	{ "ident",				required_argument, NULL, 'i' },
85 85
 	{ "daemon",				required_argument, NULL, 'd' },
@@ -110,6 +110,7 @@ static const struct option long_options[] = {
110 110
 	{ "output-nit-pass",	no_argument,       NULL, 'y' },
111 111
 	{ "output-eit-pass",	no_argument,       NULL, 'w' },
112 112
 	{ "output-tdt-pass",	no_argument,       NULL, 'x' },
113
+	{ "output-enc-pass",	no_argument,       NULL, '3' },
113 114
 
114 115
 	{ "ca-system",			required_argument, NULL, 'c' },
115 116
 	{ "caid",				required_argument, NULL, 'C' },
@@ -194,6 +195,7 @@ static void show_help(struct ts *ts) {
194 195
 	printf(" -g --output-tos <tos>      | Set TOS value of output packets. Default: none\n");
195 196
 	printf(" -u --no-output-on-error    | Do not output data when the code word is missing.\n");
196 197
 	printf(" -p --no-output-filter      | Disable output filtering. Default: %s\n", ts->pid_filter ? "enabled" : "disabled");
198
+	printf(" -3 --output-enc-pass       | Output the stream even if can not be decrypted.\n");
197 199
 	printf(" -y --output-nit-pass       | Pass through NIT.\n");
198 200
 	printf(" -w --output-eit-pass       | Pass through EIT (EPG).\n");
199 201
 	printf(" -x --output-tdt-pass       | Pass through TDT/TOT.\n");
@@ -406,6 +408,9 @@ static void parse_options(struct ts *ts, int argc, char **argv) {
406 408
 			case 'x': // --output-tdt-pass
407 409
 				ts->tdt_passthrough = !ts->tdt_passthrough;
408 410
 				break;
411
+			case '3': // --output-enc-pass
412
+				ts->allow_encrypted_output = !ts->allow_encrypted_output;
413
+				break;
409 414
 			case 'c': // --ca-system
410 415
 				if (strcasecmp("IRDETO", optarg) == 0)
411 416
 					ts->req_CA_sys = CA_IRDETO;

Loading…
Cancel
Save