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
    races if the scripts are used for logging.
6
    races if the scripts are used for logging.
7
  * Start passing _INPUT_ADDR and _OUTPUT_ADDR variables to notify script.
7
  * Start passing _INPUT_ADDR and _OUTPUT_ADDR variables to notify script.
8
  * Add --status-file (-0) option. This file keeps latest program status.
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
 2013-09-12 : Version 10.0
14
 2013-09-12 : Version 10.0
11
  * Add --ecm-only (-v) option. This allows processing of ECMs but without
15
  * Add --ecm-only (-v) option. This allows processing of ECMs but without

+ 1
- 0
README View File

134
  -g --output-tos <tos>      | Set TOS value of output packets. Default: none
134
  -g --output-tos <tos>      | Set TOS value of output packets. Default: none
135
  -u --no-output-on-error    | Do not output data when the code word is missing.
135
  -u --no-output-on-error    | Do not output data when the code word is missing.
136
  -p --no-output-filter      | Disable output filtering. Default: enabled
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
  -y --output-nit-pass       | Pass through NIT.
138
  -y --output-nit-pass       | Pass through NIT.
138
  -w --output-eit-pass       | Pass through EIT (EPG).
139
  -w --output-eit-pass       | Pass through EIT (EPG).
139
  -x --output-tdt-pass       | Pass through TDT/TOT.
140
  -x --output-tdt-pass       | Pass through TDT/TOT.

+ 102
- 0
bitstream.h View File

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
 #include "data.h"
19
 #include "data.h"
20
 #include "csa.h"
20
 #include "csa.h"
21
 #include "camd.h"
21
 #include "camd.h"
22
+#include "util.h"
22
 
23
 
23
 void data_init(struct ts *ts) {
24
 void data_init(struct ts *ts) {
24
 	memset(ts, 0, sizeof(struct ts));
25
 	memset(ts, 0, sizeof(struct ts));
99
 	ts->cw_last_warn= ts->cw_last_warn + ts->cw_warn_sec;
100
 	ts->cw_last_warn= ts->cw_last_warn + ts->cw_warn_sec;
100
 	ts->key.ts      = time(NULL);
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
 	ts->input.fd    = 0; // STDIN
108
 	ts->input.fd    = 0; // STDIN
103
 	ts->input.type  = FILE_IO;
109
 	ts->input.type  = FILE_IO;
104
 
110
 

+ 5
- 0
data.h View File

291
 	time_t				last_not_scrambled_packet_ts;
291
 	time_t				last_not_scrambled_packet_ts;
292
 	time_t				last_not_scrambled_report_ts;
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
 	unsigned int		pid_report;
299
 	unsigned int		pid_report;
295
 	unsigned int		pid_stats[MAX_PIDS];
300
 	unsigned int		pid_stats[MAX_PIDS];
296
 
301
 

+ 62
- 0
process.c View File

16
 #include <string.h>
16
 #include <string.h>
17
 #include <sys/uio.h>
17
 #include <sys/uio.h>
18
 
18
 
19
+#include "bitstream.h"
19
 #include "data.h"
20
 #include "data.h"
20
 #include "csa.h"
21
 #include "csa.h"
21
 #include "tables.h"
22
 #include "tables.h"
259
 	return NULL;
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
 static inline void output_write(struct ts *ts, uint8_t *data, unsigned int data_size) {
295
 static inline void output_write(struct ts *ts, uint8_t *data, unsigned int data_size) {
263
 	if (!data)
296
 	if (!data)
264
 		return;
297
 		return;
266
 		return;
299
 		return;
267
 	if (ts->no_output_on_error && !ts->camd.key->is_valid_cw)
300
 	if (ts->no_output_on_error && !ts->camd.key->is_valid_cw)
268
 		return;
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
 	if (!ts->rtp_output) {
331
 	if (!ts->rtp_output) {
270
 		if (write(ts->output.fd, data, data_size) < 0) {
332
 		if (write(ts->output.fd, data, data_size) < 0) {
271
 			perror("write(output_fd)");
333
 			perror("write(output_fd)");

+ 9
- 0
tsdecrypt.1 View File

175
 PAT/PMT/SDT and data packets are left in the output. Everything else not
175
 PAT/PMT/SDT and data packets are left in the output. Everything else not
176
 mentioned in PMT like NIT, EIT, TDT tables and unknown pids is removed.
176
 mentioned in PMT like NIT, EIT, TDT tables and unknown pids is removed.
177
 .TP
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
 \fB\-y\fR, \fB\-\-output\-nit\-pass\fR
183
 \fB\-y\fR, \fB\-\-output\-nit\-pass\fR
179
 Pass through NIT packets when output filtering is enabled.
184
 Pass through NIT packets when output filtering is enabled.
180
 .TP
185
 .TP
393
 
398
 
394
   \fBINPUT_OK\fR        The data have appeared on the input.
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
   \fBSTOP\fR            tsdecrypt was stopped.
405
   \fBSTOP\fR            tsdecrypt was stopped.
397
 
406
 
398
 See \fBnotify\-script.example\fR for an example on how to create external
407
 See \fBnotify\-script.example\fR for an example on how to create external

+ 7
- 2
tsdecrypt.c View File

77
 		LOG(msg);
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
 static const struct option long_options[] = {
83
 static const struct option long_options[] = {
84
 	{ "ident",				required_argument, NULL, 'i' },
84
 	{ "ident",				required_argument, NULL, 'i' },
85
 	{ "daemon",				required_argument, NULL, 'd' },
85
 	{ "daemon",				required_argument, NULL, 'd' },
110
 	{ "output-nit-pass",	no_argument,       NULL, 'y' },
110
 	{ "output-nit-pass",	no_argument,       NULL, 'y' },
111
 	{ "output-eit-pass",	no_argument,       NULL, 'w' },
111
 	{ "output-eit-pass",	no_argument,       NULL, 'w' },
112
 	{ "output-tdt-pass",	no_argument,       NULL, 'x' },
112
 	{ "output-tdt-pass",	no_argument,       NULL, 'x' },
113
+	{ "output-enc-pass",	no_argument,       NULL, '3' },
113
 
114
 
114
 	{ "ca-system",			required_argument, NULL, 'c' },
115
 	{ "ca-system",			required_argument, NULL, 'c' },
115
 	{ "caid",				required_argument, NULL, 'C' },
116
 	{ "caid",				required_argument, NULL, 'C' },
194
 	printf(" -g --output-tos <tos>      | Set TOS value of output packets. Default: none\n");
195
 	printf(" -g --output-tos <tos>      | Set TOS value of output packets. Default: none\n");
195
 	printf(" -u --no-output-on-error    | Do not output data when the code word is missing.\n");
196
 	printf(" -u --no-output-on-error    | Do not output data when the code word is missing.\n");
196
 	printf(" -p --no-output-filter      | Disable output filtering. Default: %s\n", ts->pid_filter ? "enabled" : "disabled");
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
 	printf(" -y --output-nit-pass       | Pass through NIT.\n");
199
 	printf(" -y --output-nit-pass       | Pass through NIT.\n");
198
 	printf(" -w --output-eit-pass       | Pass through EIT (EPG).\n");
200
 	printf(" -w --output-eit-pass       | Pass through EIT (EPG).\n");
199
 	printf(" -x --output-tdt-pass       | Pass through TDT/TOT.\n");
201
 	printf(" -x --output-tdt-pass       | Pass through TDT/TOT.\n");
406
 			case 'x': // --output-tdt-pass
408
 			case 'x': // --output-tdt-pass
407
 				ts->tdt_passthrough = !ts->tdt_passthrough;
409
 				ts->tdt_passthrough = !ts->tdt_passthrough;
408
 				break;
410
 				break;
411
+			case '3': // --output-enc-pass
412
+				ts->allow_encrypted_output = !ts->allow_encrypted_output;
413
+				break;
409
 			case 'c': // --ca-system
414
 			case 'c': // --ca-system
410
 				if (strcasecmp("IRDETO", optarg) == 0)
415
 				if (strcasecmp("IRDETO", optarg) == 0)
411
 					ts->req_CA_sys = CA_IRDETO;
416
 					ts->req_CA_sys = CA_IRDETO;

Loading…
Cancel
Save