Browse Source

Add newcamd protocol support.

Georgi Chorbadzhiyski 12 years ago
parent
commit
ff5356a74f
11 changed files with 539 additions and 14 deletions
  1. 1
    0
      ChangeLog
  2. 2
    1
      Makefile
  3. 6
    2
      README
  4. 420
    0
      camd-newcamd.c
  5. 1
    0
      camd.h
  6. 1
    0
      data.c
  7. 29
    0
      data.h
  8. 15
    5
      tsdecrypt.1
  9. 44
    6
      tsdecrypt.c
  10. 19
    0
      util.c
  11. 1
    0
      util.h

+ 1
- 0
ChangeLog View File

@@ -10,6 +10,7 @@ xxxx-xx-xx : Version next
10 10
  * Add --output-nit-pass (-y) to enable NIT pass through.
11 11
  * Add --output-eit-pass (-w) to enable EIT (EPG) pass through.
12 12
  * Add --output-tdt-pass (-x) to enable TDT/TOT pass through.
13
+ * Add support for newcamd CAMD protocol.
13 14
 
14 15
 2011-11-18 : Version 4.0
15 16
  * Set CAMD sockets NODELAY to avoid OSCAM errors when many packets are sent.

+ 2
- 1
Makefile View File

@@ -40,11 +40,12 @@ tsdecrypt_SRC = data.c \
40 40
  util.c \
41 41
  camd.c \
42 42
  camd-cs378x.c \
43
+ camd-newcamd.c \
43 44
  process.c \
44 45
  tables.c \
45 46
  notify.c \
46 47
  tsdecrypt.c
47
-tsdecrypt_LIBS = -lcrypto -ldvbcsa -lpthread
48
+tsdecrypt_LIBS = -lcrypt -lcrypto -ldvbcsa -lpthread
48 49
 tsdecrypt_OBJS = $(FUNCS_LIB) $(TS_LIB) $(tsdecrypt_SRC:.c=.o)
49 50
 
50 51
 CLEAN_OBJS = tsdecrypt $(tsdecrypt_SRC:.c=.{o,d})

+ 6
- 2
README View File

@@ -2,8 +2,8 @@ tsdecrypt
2 2
 =========
3 3
 tsdecrypt reads incoming mpeg transport stream over UDP/RTP and then
4 4
 decrypts it using libdvbcsa and keys obtained from OSCAM or similar cam
5
-server. tsdecrypt communicates with CAM server using camd35 over tcp
6
-protocol also known as cs378x.
5
+server. tsdecrypt communicates with CAM server using cs378x (camd35 over
6
+tcp) protocol or newcamd protocol.
7 7
 
8 8
 License
9 9
 =======
@@ -58,9 +58,13 @@ CA options:
58 58
  -C --caid <caid>           | Set CAID. Default: Taken from --ca-system.
59 59
 
60 60
 CAMD server options:
61
+ -A --camd-proto <proto>    | Set CAMD network protocol.
62
+                            . Valid protocols are: CS378X (default) and NEWCAMD
61 63
  -s --camd-server <addr>    | Set CAMD server ip_address:port (1.2.3.4:2233).
62 64
  -U --camd-user <user>      | Set CAMD server user. Default: user
63 65
  -P --camd-pass <pass>      | Set CAMD server password. Default: pass
66
+ -B --camd-des-key <key>    | Set DES key for newcamd protocol.
67
+                            . Default: 0102030405060708091011121314
64 68
 
65 69
 EMM options:
66 70
  -e --emm                   | Enable sending EMM's to CAMD. Default: disabled

+ 420
- 0
camd-newcamd.c View File

@@ -0,0 +1,420 @@
1
+/*
2
+ * newcamd protocol
3
+ * Most of the code is copied from getstream's newcamd protocol implementation.
4
+ *
5
+ * This program is free software; you can redistribute it and/or modify
6
+ * it under the terms of the GNU General Public License version 2
7
+ * as published by the Free Software Foundation.
8
+ *
9
+ * This program is distributed in the hope that it will be useful,
10
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
+ * GNU General Public License for more details.
13
+ *
14
+ * You should have received a copy of the GNU General Public License
15
+ * along with this program; if not, write to the Free Software
16
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
17
+ */
18
+
19
+#define _XOPEN_SOURCE 700 // Needed to pull crypt() from unistd.h
20
+#define _GNU_SOURCE 1 // Needed to pull crypt_r() from crypt.h
21
+
22
+#include <stdlib.h>
23
+#include <string.h>
24
+#include <unistd.h>
25
+#include <crypt.h>
26
+
27
+#include <openssl/des.h>
28
+
29
+#include "libfuncs/libfuncs.h"
30
+
31
+#include "data.h"
32
+#include "util.h"
33
+#include "camd.h"
34
+
35
+#define NEWCAMD_PROTO_VER      525
36
+#define NEWCAMD_HDR_LEN        8
37
+#define NEWCAMD_FIRST_CMD_NO   0xe0
38
+
39
+typedef enum {
40
+	MSG_CLIENT_2_SERVER_LOGIN = NEWCAMD_FIRST_CMD_NO,
41
+	MSG_CLIENT_2_SERVER_LOGIN_ACK,
42
+	MSG_CLIENT_2_SERVER_LOGIN_NAK,
43
+	MSG_CARD_DATA_REQ,
44
+	MSG_CARD_DATA,
45
+	MSG_SERVER_2_CLIENT_NAME,
46
+	MSG_SERVER_2_CLIENT_NAME_ACK,
47
+	MSG_SERVER_2_CLIENT_NAME_NAK,
48
+	MSG_SERVER_2_CLIENT_LOGIN,
49
+	MSG_SERVER_2_CLIENT_LOGIN_ACK,
50
+	MSG_SERVER_2_CLIENT_LOGIN_NAK,
51
+	MSG_ADMIN,
52
+	MSG_ADMIN_ACK,
53
+	MSG_ADMIN_LOGIN,
54
+	MSG_ADMIN_LOGIN_ACK,
55
+	MSG_ADMIN_LOGIN_NAK,
56
+	MSG_ADMIN_COMMAND,
57
+	MSG_ADMIN_COMMAND_ACK,
58
+	MSG_ADMIN_COMMAND_NAK,
59
+	MSG_KEEPALIVE = NEWCAMD_FIRST_CMD_NO + 0x1d
60
+} net_msg_type_t;
61
+
62
+static void set_odd_parity(uint8_t *key) {
63
+	DES_set_odd_parity((DES_cblock *)&key[0]);
64
+	DES_set_odd_parity((DES_cblock *)&key[8]);
65
+}
66
+
67
+static void des_schedule_key(triple_des_t *td) {
68
+	DES_key_sched((DES_cblock *)&td->des_key[0],&td->ks1);
69
+	DES_key_sched((DES_cblock *)&td->des_key[8],&td->ks2);
70
+}
71
+
72
+static void des_key_spread(triple_des_t *td, uint8_t *normal) {
73
+	td->des_key[0]  =   normal[0] & 0xfe;
74
+	td->des_key[1]  = ((normal[0] << 7) | (normal[1] >> 1)) & 0xfe;
75
+	td->des_key[2]  = ((normal[1] << 6) | (normal[2] >> 2)) & 0xfe;
76
+	td->des_key[3]  = ((normal[2] << 5) | (normal[3] >> 3)) & 0xfe;
77
+	td->des_key[4]  = ((normal[3] << 4) | (normal[4] >> 4)) & 0xfe;
78
+	td->des_key[5]  = ((normal[4] << 3) | (normal[5] >> 5)) & 0xfe;
79
+	td->des_key[6]  = ((normal[5] << 2) | (normal[6] >> 6)) & 0xfe;
80
+	td->des_key[7]  =   normal[6] << 1;
81
+	td->des_key[8]  =   normal[7] & 0xfe;
82
+	td->des_key[9]  = ((normal[7] << 7)  | (normal[8] >> 1)) & 0xfe;
83
+	td->des_key[10] = ((normal[8] << 6)  | (normal[9] >> 2)) & 0xfe;
84
+	td->des_key[11] = ((normal[9] << 5)  | (normal[10] >> 3)) & 0xfe;
85
+	td->des_key[12] = ((normal[10] << 4) | (normal[11] >> 4)) & 0xfe;
86
+	td->des_key[13] = ((normal[11] << 3) | (normal[12] >> 5)) & 0xfe;
87
+	td->des_key[14] = ((normal[12] << 2) | (normal[13] >> 6)) & 0xfe;
88
+	td->des_key[15] =   normal[13] << 1;
89
+	set_odd_parity(td->des_key);
90
+};
91
+
92
+static uint8_t xor_sum(const uint8_t *mem, int len) {
93
+	uint8_t cs = 0;
94
+	while (len > 0) {
95
+		cs ^= *mem++;
96
+		len--;
97
+	}
98
+	return cs;
99
+}
100
+
101
+static int pad_message(uint8_t *data, int len) {
102
+	DES_cblock padBytes;
103
+	uint8_t noPadBytes = (8 - ((len - 1) % 8)) % 8;
104
+	if (len + noPadBytes + 1 >= NEWCAMD_MSG_SIZE - 8)
105
+		return -1;
106
+	DES_random_key(&padBytes);
107
+	memcpy(data + len, padBytes, noPadBytes);
108
+	len += noPadBytes;
109
+	data[len] = xor_sum(data + 2, len - 2);
110
+	return len + 1;
111
+}
112
+
113
+static const uint8_t *triple_des_encrypt(triple_des_t *td, uint8_t *data, int len, uint8_t *crypted) {
114
+	DES_cblock ivec;
115
+	DES_random_key(&ivec);
116
+	memcpy(crypted + len, ivec, sizeof(ivec));
117
+	DES_ede2_cbc_encrypt(data + 2, crypted + 2, len - 2, &td->ks1, &td->ks2, (DES_cblock *)ivec, DES_ENCRYPT);
118
+	return crypted;
119
+}
120
+
121
+static void triple_des_decrypt(triple_des_t *td, uint8_t *data, int len) {
122
+	if ((len-2) % 8 || (len-2) < 16)
123
+		return;
124
+	DES_cblock ivec;
125
+	len -= sizeof(ivec);
126
+	memcpy(ivec, data+len, sizeof(ivec));
127
+	DES_ede2_cbc_encrypt(data+2, data+2, len-2, &td->ks1, &td->ks2, (DES_cblock *)ivec, DES_DECRYPT);
128
+}
129
+
130
+static void prepare_login_key(struct camd *c, const uint8_t *rkey) {
131
+	unsigned int i;
132
+	uint8_t tmpkey[14];
133
+	for(i = 0; i < sizeof(tmpkey); i++)
134
+		tmpkey[i] = rkey[i] ^ c->newcamd.bin_des_key[i];
135
+	des_key_spread(&c->newcamd.td_key, tmpkey);
136
+}
137
+
138
+static int newcamd_connect(struct camd *c);
139
+
140
+static uint8_t newcamd_send_msg(struct camd *c, const uint8_t *data, int data_len, uint16_t service_id, uint8_t useMsgId) {
141
+	uint8_t netbuf[NEWCAMD_MSG_SIZE];
142
+
143
+	if (newcamd_connect(c) < 0)
144
+		return 0;
145
+
146
+	if (data_len < 3 || (data_len + NEWCAMD_HDR_LEN + 4) > NEWCAMD_MSG_SIZE) {
147
+		ts_LOGf("ERR | [%s] Bad message size.\n", c->ops.ident);
148
+		return 0; // false
149
+	}
150
+
151
+	memset(&netbuf[2], 0, NEWCAMD_HDR_LEN + 2);
152
+	memcpy(&netbuf[NEWCAMD_HDR_LEN + 4], data, data_len);
153
+
154
+	netbuf[NEWCAMD_HDR_LEN + 4 + 1] = (data[1] & 0xF0) | (((data_len - 3) >> 8) & 0x0F);
155
+	netbuf[NEWCAMD_HDR_LEN + 4 + 2] = (data_len - 3) & 0xFF;
156
+
157
+	data_len += 4;
158
+	netbuf[4] = service_id >> 8;
159
+	netbuf[5] = service_id & 0xFF;
160
+
161
+	data_len += NEWCAMD_HDR_LEN;
162
+
163
+	if (useMsgId) {
164
+		c->newcamd.msg_id++;
165
+		netbuf[2] = c->newcamd.msg_id >> 8;
166
+		netbuf[3] = c->newcamd.msg_id & 0xFF;
167
+	}
168
+
169
+	if ((data_len = pad_message(netbuf, data_len)) < 0) {
170
+		ts_LOGf("ERR | [%s] Pad_message failed.\n", c->ops.ident);
171
+		return 0;
172
+	}
173
+
174
+	if ((data = triple_des_encrypt(&c->newcamd.td_key, netbuf, data_len, netbuf)) == 0) {
175
+		ts_LOGf("ERR | [%s] Encrypt failed.\n", c->ops.ident);
176
+		return 0;
177
+	}
178
+
179
+	data_len += sizeof(DES_cblock);
180
+	netbuf[0] = (data_len - 2) >> 8;
181
+	netbuf[1] = (data_len - 2) & 0xFF;
182
+
183
+	return fdwrite(c->server_fd, (char *)netbuf, data_len);
184
+}
185
+
186
+static int newcamd_recv_msg(struct camd *c, uint8_t *data, uint8_t useMsgId) {
187
+	uint8_t *netbuf = c->newcamd.buf;
188
+
189
+	if (fdread(c->server_fd, (char *)netbuf, 2) != 2) {
190
+		ts_LOGf("ERR | [%s] Failed to read message.\n", c->ops.ident);
191
+		return 0;
192
+	}
193
+
194
+	int mlen = ((netbuf[0] << 8) | netbuf[1]) & 0xFFFF;
195
+	if (mlen > NEWCAMD_MSG_SIZE - 2) {
196
+		ts_LOGf("ERR | [%s] Buffer overflow [mlen = %d]\n", c->ops.ident, mlen);
197
+		return 0;
198
+	}
199
+
200
+	if (fdread(c->server_fd, (char *)netbuf+2, mlen) != mlen) {
201
+		ts_LOGf("ERR | [%s] Failed to read message.\n", c->ops.ident);
202
+		return 0;
203
+	}
204
+
205
+	mlen += 2;
206
+	triple_des_decrypt(&c->newcamd.td_key, netbuf, mlen);
207
+	mlen -= sizeof(DES_cblock);
208
+	if (xor_sum(netbuf + 2, mlen - 2)) {
209
+		ts_LOGf("ERR | [%s] Checksum error.\n", c->ops.ident);
210
+		return 0;
211
+	}
212
+
213
+	int retlen = (((netbuf[5 + NEWCAMD_HDR_LEN] << 8) | netbuf[6 + NEWCAMD_HDR_LEN]) & 0x0FFF) + 3;
214
+
215
+	if (useMsgId) {
216
+		uint16_t tmp = ((netbuf[2] << 8) | netbuf[3]) & 0xFFFF;
217
+		if (c->newcamd.msg_id != tmp) {
218
+			ts_LOGf("ERR | [%s] Bad msg_id %04X != %04X\n", c->ops.ident, c->newcamd.msg_id, tmp);
219
+			return -2;
220
+		}
221
+	}
222
+
223
+	memmove(data, netbuf + 4 + NEWCAMD_HDR_LEN, retlen);
224
+
225
+	return retlen;
226
+}
227
+
228
+static uint8_t newcamd_send_cmd(struct camd *c, net_msg_type_t cmd) {
229
+	uint8_t data[3] = { 0, 0, 0 };
230
+	data[0] = cmd;
231
+	return newcamd_send_msg(c, data, sizeof(data), 0, 0);
232
+}
233
+
234
+static int newcamd_recv_cmd(struct camd *c) {
235
+	uint8_t buffer[NEWCAMD_MSG_SIZE];
236
+	if (newcamd_recv_msg(c, buffer, 0) != 3)
237
+		return -1;
238
+	return buffer[0];
239
+}
240
+
241
+static void newcamd_init_card_data(struct camd *c, struct newcamd *nc, uint8_t *buffer) {
242
+	int i;
243
+	char msg_buffer[32];
244
+
245
+	nc->caid = (buffer[4] << 8) | buffer[5];
246
+
247
+	for(i = 0; i < 8; i++)
248
+		sprintf(&msg_buffer[i*2], "%02X", buffer[6 + i]);
249
+
250
+	ts_LOGf("CAM | [%s] Card info: CAID 0x%04X Admin=%s srvUA=%s\n",
251
+		c->ops.ident, nc->caid, (buffer[3]==1) ? "YES" : "NO", msg_buffer);
252
+	memcpy(nc->ua, &buffer[6], 8);
253
+
254
+	// Parse providers
255
+	uint8_t is_ok = 0;
256
+	nc->num_of_provs = buffer[14];
257
+	for (i = 0; i < nc->num_of_provs; i++) {
258
+		if (nc->prov_ident_manual == 0) {
259
+			memcpy(nc->provs_ident[i], &buffer[15+11*i], 3);
260
+			memcpy(nc->provs_id[i], &buffer[18+11*i], 8);
261
+		} else {
262
+			if (!memcmp(nc->provs_ident[0], &buffer[15+11*i], 3)) {
263
+				is_ok = 1;
264
+				memcpy(nc->provs_id[0], &buffer[18+11*i], 8);
265
+			} else {
266
+				continue;
267
+			}
268
+		}
269
+		uint8_t debug_idx = (nc->prov_ident_manual == 1) ? 0 : i;
270
+		ts_LOGf("CAM | [%s] Card info: Provider %d : %02X%02X%02X : %02X%02X%02X%02X%02X%02X%02X%02X\n",
271
+				c->ops.ident,
272
+				debug_idx,
273
+				nc->provs_ident[debug_idx][0], nc->provs_ident[debug_idx][1],
274
+				nc->provs_ident[debug_idx][2], nc->provs_id[debug_idx][0],
275
+				nc->provs_id[debug_idx][1], nc->provs_id[debug_idx][2],
276
+				nc->provs_id[debug_idx][3], nc->provs_id[debug_idx][4],
277
+				nc->provs_id[debug_idx][5], nc->provs_id[debug_idx][6],
278
+				nc->provs_id[debug_idx][7]);
279
+
280
+		if (nc->prov_ident_manual == 1 && is_ok == 1) {
281
+			nc->num_of_provs = 1;
282
+			break;
283
+		}
284
+	}
285
+}
286
+
287
+static int newcamd_login(struct camd *c) {
288
+	uint8_t *buffer = c->newcamd.buf;
289
+
290
+	c->newcamd.caid = 0;
291
+	c->newcamd.msg_id = 0;
292
+
293
+	uint8_t rand_data[14];
294
+	if (fdread(c->server_fd, (char *)rand_data, sizeof(rand_data)) != 14) {
295
+		ts_LOGf("ERR | [%s] Can't read protocol handshake.\n", c->ops.ident);
296
+		return 0;
297
+	}
298
+
299
+	struct crypt_data crypt_data;
300
+	crypt_data.initialized = 0;
301
+	char *crPasswd = crypt_r(c->pass, "$1$abcdefgh$", &crypt_data);
302
+
303
+	const int userLen = strlen(c->user) + 1;
304
+	const int passLen = strlen(crPasswd) + 1;
305
+
306
+	// prepare login message
307
+	buffer[0] = MSG_CLIENT_2_SERVER_LOGIN;
308
+	buffer[1] = 0;
309
+	buffer[2] = userLen + passLen;
310
+	memcpy(&buffer[3], c->user, userLen);
311
+	memcpy(&buffer[3 + userLen], crPasswd, passLen);
312
+
313
+	prepare_login_key(c, rand_data);
314
+	des_schedule_key(&c->newcamd.td_key);
315
+
316
+	if (!newcamd_send_msg(c, buffer, buffer[2] + 3, 0, 1) ||
317
+		newcamd_recv_cmd(c) != MSG_CLIENT_2_SERVER_LOGIN_ACK)
318
+	{
319
+		ts_LOGf("ERR | [%s] Login failed. Check user/pass/des-key.\n", c->ops.ident);
320
+		sleep(1);
321
+		return 0;
322
+	}
323
+
324
+	// Prepare session key
325
+	uint8_t tmpkey[14];
326
+	memcpy(tmpkey, c->newcamd.bin_des_key, sizeof(tmpkey));
327
+	int i;
328
+	for(i = 0; i < (passLen - 1); ++i)
329
+		tmpkey[i % 14] ^= crPasswd[i];
330
+	des_key_spread(&c->newcamd.td_key, tmpkey);
331
+	des_schedule_key(&c->newcamd.td_key);
332
+
333
+	if (!newcamd_send_cmd(c, MSG_CARD_DATA_REQ) || newcamd_recv_msg(c, buffer, 0) <= 0) {
334
+		ts_LOGf("ERR | [%s] MSG_CARD_DATA_REQ error.\n", c->ops.ident);
335
+		return 0;
336
+	}
337
+
338
+	if (buffer[0] == MSG_CARD_DATA) {
339
+		newcamd_init_card_data(c, &c->newcamd, buffer);
340
+	} else {
341
+		ts_LOGf("ERR | [%s] MSG_CARD_DATA response error.\n", c->ops.ident);
342
+	}
343
+
344
+	return 1;
345
+}
346
+
347
+static int newcamd_connect(struct camd *c) {
348
+	if (c->server_fd < 0) {
349
+		c->server_fd = camd_tcp_connect(c->server_addr, c->server_port);
350
+		if (!newcamd_login(c))
351
+			shutdown_fd(&c->server_fd);
352
+	}
353
+	return c->server_fd;
354
+}
355
+
356
+static void newcamd_disconnect(struct camd *c) {
357
+	shutdown_fd(&c->server_fd);
358
+}
359
+
360
+static int newcamd_reconnect(struct camd *c) {
361
+	newcamd_disconnect(c);
362
+	return newcamd_connect(c);
363
+}
364
+
365
+static int newcamd_do_ecm(struct camd *c, struct camd_msg *msg) {
366
+	return newcamd_send_msg(c, msg->data, msg->data_len, msg->service_id, 1);
367
+}
368
+
369
+static int newcamd_do_emm(struct camd *c, struct camd_msg *msg) {
370
+	uint8_t *buf = c->newcamd.buf;
371
+	int ret;
372
+
373
+	ret = newcamd_send_msg(c, msg->data, msg->data_len, msg->service_id, 1);
374
+	if (!ret)
375
+		return ret;
376
+
377
+	int data_len = newcamd_recv_msg(c, buf, 1);
378
+	if (data_len >= 3) {
379
+		if (buf[1] & 0x10)
380
+			return 1; // OK
381
+		ts_LOGf("ERR | [%s] EMM rejected by card.\n", c->ops.ident);
382
+	} else {
383
+		ts_LOGf("ERR | [%s] EMM unexpected server response (data_len=%d, buf[1]=0x%02x).\n",
384
+			c->ops.ident, data_len, buf[1]);
385
+	}
386
+
387
+	return 0; // Error
388
+}
389
+
390
+static int newcamd_get_cw(struct camd *c, uint16_t *ca_id, uint16_t *idx, uint8_t *cw) {
391
+	int ret;
392
+	uint8_t *buf = c->newcamd.buf;
393
+
394
+	while((ret = newcamd_recv_msg(c, buf, 1)) == -2)
395
+		ts_LOGf("ERR | [%s] msg_id sync error. retrying...\n", c->ops.ident);
396
+
397
+	if (ret != 19) {
398
+		if (ret == 3)
399
+			ts_LOGf("ERR | [%s] Card was not able to decode the channel.\n", c->ops.ident);
400
+		else
401
+			ts_LOGf("ERR | [%s] Unexpected CAMD server response (code %d).\n", c->ops.ident, ret);
402
+		return 0;
403
+	}
404
+
405
+	*ca_id = c->newcamd.caid;
406
+	*idx   = 0; // FIXME
407
+	memcpy(cw, c->newcamd.buf + 3, 16);
408
+	return 1;
409
+}
410
+
411
+void camd_proto_newcamd(struct camd_ops *ops) {
412
+	strcpy(ops->ident, "newcamd");
413
+	ops->proto		= CAMD_NEWCAMD;
414
+	ops->connect	= newcamd_connect;
415
+	ops->disconnect	= newcamd_disconnect;
416
+	ops->reconnect	= newcamd_reconnect;
417
+	ops->do_emm		= newcamd_do_emm;
418
+	ops->do_ecm		= newcamd_do_ecm;
419
+	ops->get_cw		= newcamd_get_cw;
420
+}

+ 1
- 0
camd.h View File

@@ -31,5 +31,6 @@ void					camd_stop			(struct ts *ts);
31 31
 void					camd_process_packet	(struct ts *ts, struct camd_msg *msg);
32 32
 
33 33
 void					camd_proto_cs378x	(struct camd_ops *ops);
34
+void					camd_proto_newcamd	(struct camd_ops *ops);
34 35
 
35 36
 #endif

+ 1
- 0
data.c View File

@@ -67,6 +67,7 @@ void data_init(struct ts *ts) {
67 67
 	ts->camd.key          = &ts->key;
68 68
 	strcpy(ts->camd.user, "user");
69 69
 	strcpy(ts->camd.pass, "pass");
70
+	strcpy(ts->camd.newcamd.hex_des_key, "0102030405060708091011121314");
70 71
 
71 72
 	camd_proto_cs378x(&ts->camd.ops);
72 73
 

+ 29
- 0
data.h View File

@@ -22,6 +22,7 @@
22 22
 #include <limits.h>
23 23
 
24 24
 #include <openssl/aes.h>
25
+#include <openssl/des.h>
25 26
 #include <openssl/md5.h>
26 27
 
27 28
 #include <dvbcsa/dvbcsa.h>
@@ -77,6 +78,7 @@ struct camd_msg {
77 78
 
78 79
 enum camd_proto {
79 80
 	CAMD_CS378X,
81
+	CAMD_NEWCAMD,
80 82
 };
81 83
 
82 84
 struct camd_ops {
@@ -99,6 +101,32 @@ struct cs378x {
99 101
 	uint16_t		msg_id;
100 102
 };
101 103
 
104
+#define DESKEY_LENGTH     28
105
+#define NEWCAMD_MSG_SIZE  400
106
+#define NEWCAMD_MAXPROV   32
107
+
108
+typedef struct {
109
+	DES_key_schedule ks1;
110
+	DES_key_schedule ks2;
111
+	uint8_t des_key[16];
112
+} triple_des_t;
113
+
114
+struct newcamd {
115
+	// newcamd private data
116
+	uint8_t			buf[NEWCAMD_MSG_SIZE];
117
+	char			hex_des_key[DESKEY_LENGTH + 1];
118
+	uint8_t			bin_des_key[DESKEY_LENGTH / 2];	// Decoded des_key
119
+	triple_des_t	td_key;
120
+	uint16_t		msg_id;
121
+	// Initialized from CARD INFO command
122
+	int				caid;
123
+	uint8_t			ua[8];
124
+	uint8_t			num_of_provs;
125
+	uint8_t			provs_ident[NEWCAMD_MAXPROV][3];
126
+	uint8_t			provs_id[NEWCAMD_MAXPROV][8];
127
+	uint8_t			prov_ident_manual;
128
+};
129
+
102 130
 struct camd {
103 131
 	int				server_fd;
104 132
 	struct in_addr	server_addr;
@@ -118,6 +146,7 @@ struct camd {
118 146
 
119 147
 	struct camd_ops	ops;
120 148
 	struct cs378x	cs378x;
149
+	struct newcamd	newcamd;
121 150
 };
122 151
 
123 152
 enum io_type {

+ 15
- 5
tsdecrypt.1 View File

@@ -5,9 +5,10 @@ tsdecrypt - Decrypt mpeg transport stream.
5 5
 .B tsdecrypt
6 6
 [\fIoptions\fR]
7 7
 .SH DESCRIPTION
8
-tsdecrypt reads incoming mpeg transport stream over UDP/RTP and then decrypts it
9
-using libdvbcsa and keys obtained from OSCAM or similar cam server. tsdecrypt
10
-communicates with CAM server using camd35 over tcp protocol also known as cs378x.
8
+tsdecrypt reads incoming mpeg transport stream over UDP/RTP or file and
9
+then decrypts it using libdvbcsa and keys obtained from OSCAM or similar
10
+CAMD server. tsdecrypt communicates with CAM server using cs378x (camd35
11
+over tcp) protocol or newcamd protocol.
11 12
 .SH OPTIONS
12 13
 .TP
13 14
 .SH MAIN OPTIONS
@@ -122,10 +123,15 @@ one CA but with different CAIDs.
122 123
 .SH CAMD OPTIONS
123 124
 .PP
124 125
 .TP
126
+\fB\-A\fR, \fB\-\-camd\-proto\fR <protocol>
127
+Set CAMD server protocol. Valid protocols are \fBCS378X\fR and \fBNEWCAMD\fR.
128
+If this option is not used the default protocol is \fBCS378X\fR (camd35 over
129
+tcp).
130
+.TP
125 131
 \fB\-s\fR, \fB\-\-camd\-server\fR <addr[:port]>
126 132
 Set CAMD server ip and port (10.0.1.1:2233). Is not set default port
127
-is \fB2233\fR. tsdecrypt is tested and working with OSCAM using cs378x protocol
128
-(camd35 over tcp).
133
+is \fB2233\fR. 2233 is the default port CS378X protocol, for NEWCAMD
134
+protocol you probably should choose other port.
129 135
 .TP
130 136
 \fB\-U\fR, \fB\-\-camd\-user\fR <username>
131 137
 Set CAMD user name. The default is \fBuser\fR.
@@ -133,6 +139,10 @@ Set CAMD user name. The default is \fBuser\fR.
133 139
 \fB\-P\fR, \fB\-\-camd\-pass\fR <password>
134 140
 Set CAMD user password. The default is \fBpass\fR.
135 141
 .TP
142
+\fB\-B\fR, \fB\-\-camd\-des\-key\fR <des_key>
143
+Set DES key used by NEWCAMD protocol. The default
144
+is \fB0102030405060708091011121314\fR.
145
+.TP
136 146
 .SH EMM OPTIONS
137 147
 .PP
138 148
 .TP

+ 44
- 6
tsdecrypt.c View File

@@ -103,7 +103,7 @@ void run_benchmark(void) {
103 103
 	puts("* Done *");
104 104
 }
105 105
 
106
-// Unused short options: ABFQTWYagjkmnqruv0123456789
106
+// Unused short options: FQTWYagjkmnqruv0123456789
107 107
 static const struct option long_options[] = {
108 108
 	{ "ident",				required_argument, NULL, 'i' },
109 109
 	{ "daemon",				required_argument, NULL, 'd' },
@@ -127,9 +127,12 @@ static const struct option long_options[] = {
127 127
 
128 128
 	{ "ca-system",			required_argument, NULL, 'c' },
129 129
 	{ "caid",				required_argument, NULL, 'C' },
130
+
131
+	{ "camd-proto",			required_argument, NULL, 'A' },
130 132
 	{ "camd-server",		required_argument, NULL, 's' },
131 133
 	{ "camd-user",			required_argument, NULL, 'U' },
132 134
 	{ "camd-pass",			required_argument, NULL, 'P' },
135
+	{ "camd-des-key",		required_argument, NULL, 'B' },
133 136
 
134 137
 	{ "emm",				no_argument,       NULL, 'e' },
135 138
 	{ "emm-pid",			required_argument, NULL, 'Z' },
@@ -194,9 +197,13 @@ static void show_help(struct ts *ts) {
194 197
 	printf(" -C --caid <caid>           | Set CAID. Default: Taken from --ca-system.\n");
195 198
 	printf("\n");
196 199
 	printf("CAMD server options:\n");
200
+	printf(" -A --camd-proto <proto>    | Set CAMD network protocol.\n");
201
+	printf("                            . Valid protocols are: CS378X (default) and NEWCAMD\n");
197 202
 	printf(" -s --camd-server <addr>    | Set CAMD server ip_address:port (1.2.3.4:2233).\n");
198 203
 	printf(" -U --camd-user <user>      | Set CAMD server user. Default: %s\n", ts->camd.user);
199 204
 	printf(" -P --camd-pass <pass>      | Set CAMD server password. Default: %s\n", ts->camd.pass);
205
+	printf(" -B --camd-des-key <key>    | Set DES key for newcamd protocol.\n");
206
+	printf("                            . Default: %s\n", ts->camd.newcamd.hex_des_key);
200 207
 	printf("\n");
201 208
 	printf("EMM options:\n");
202 209
 	printf(" -e --emm                   | Enable sending EMM's to CAMD. Default: %s\n", ts->emm_send ? "enabled" : "disabled");
@@ -255,8 +262,8 @@ static int parse_io_param(struct io *io, char *opt, int open_flags, mode_t open_
255 262
 }
256 263
 
257 264
 static void parse_options(struct ts *ts, int argc, char **argv) {
258
-	int j, i, ca_err = 0, server_err = 1, input_addr_err = 0, output_addr_err = 0, output_intf_err = 0, ident_err = 0;
259
-	while ( (j = getopt_long(argc, argv, "i:d:N:Sl:L:I:RzM:O:o:t:pwxyc:C:s:U:P:eZ:Ef:X:H:G:KJ:D:bhV", long_options, NULL)) != -1 ) {
265
+	int j, i, ca_err = 0, server_err = 1, input_addr_err = 0, output_addr_err = 0, output_intf_err = 0, ident_err = 0, port_set = 0;
266
+	while ( (j = getopt_long(argc, argv, "i:d:N:Sl:L:I:RzM:O:o:t:pwxyc:C:A:s:U:P:B:eZ:Ef:X:H:G:KJ:D:bhV", long_options, NULL)) != -1 ) {
260 267
 		char *p = NULL;
261 268
 		switch (j) {
262 269
 			case 'i':
@@ -348,11 +355,22 @@ static void parse_options(struct ts *ts, int argc, char **argv) {
348 355
 				ts->forced_caid = strtoul(optarg, NULL, 0) & 0xffff;
349 356
 				break;
350 357
 
358
+			case 'A':
359
+				if (strcasecmp(optarg, "cs378x") == 0) {
360
+					camd_proto_cs378x(&ts->camd.ops);
361
+				} else if (strcasecmp(optarg, "newcamd") == 0) {
362
+					camd_proto_newcamd(&ts->camd.ops);
363
+				} else {
364
+					fprintf(stderr, "Unknown CAMD protocol: %s\n", optarg);
365
+					exit(EXIT_FAILURE);
366
+				}
367
+				break;
351 368
 			case 's':
352 369
 				p = strrchr(optarg, ':');
353 370
 				if (p) {
354 371
 					*p = 0x00;
355 372
 					ts->camd.server_port = atoi(p + 1);
373
+					port_set = 1;
356 374
 				}
357 375
 				if (inet_aton(optarg, &ts->camd.server_addr) == 0)
358 376
 					server_err = 1;
@@ -367,6 +385,14 @@ static void parse_options(struct ts *ts, int argc, char **argv) {
367 385
 				strncpy(ts->camd.pass, optarg, sizeof(ts->camd.pass) - 1);
368 386
 				ts->camd.pass[sizeof(ts->camd.pass) - 1] = 0;
369 387
 				break;
388
+			case 'B':
389
+				if (strlen(optarg) != DESKEY_LENGTH) {
390
+					fprintf(stderr, "ERROR: des key should be %u characters long.\n", DESKEY_LENGTH);
391
+					exit(EXIT_FAILURE);
392
+				}
393
+				strncpy(ts->camd.newcamd.hex_des_key, optarg, sizeof(ts->camd.newcamd.hex_des_key) - 1);
394
+				ts->camd.newcamd.hex_des_key[sizeof(ts->camd.newcamd.hex_des_key) - 1] = 0;
395
+				break;
370 396
 
371 397
 			case 'e':
372 398
 				ts->emm_send = !ts->emm_send;
@@ -441,6 +467,15 @@ static void parse_options(struct ts *ts, int argc, char **argv) {
441 467
 			fprintf(stderr, "ERROR: Output interface address is invalid.\n");
442 468
 		exit(EXIT_FAILURE);
443 469
 	}
470
+	if (decode_hex_string(ts->camd.newcamd.hex_des_key, ts->camd.newcamd.bin_des_key, DESKEY_LENGTH) < 0) {
471
+		fprintf(stderr, "ERROR: Invalid hex string for des key: %s\n", ts->camd.newcamd.hex_des_key);
472
+		exit(EXIT_FAILURE);
473
+	}
474
+	if (ts->camd.ops.proto == CAMD_NEWCAMD && !port_set) {
475
+		fprintf(stderr, "ERROR: CAMD server port is not set. Use --camd-server %s:xxxx to set the port.\n", inet_ntoa(ts->camd.server_addr));
476
+		exit(EXIT_FAILURE);
477
+	}
478
+
444 479
 	ts_LOGf("Ident      : %s\n", ts->ident[0] ? ts->ident : "*NOT SET*");
445 480
 	ts_LOGf("Notify prog: %s\n", ts->notify_program[0] ? ts->notify_program : "*NOT SET*");
446 481
 	if (ts->pidfile[0])
@@ -499,9 +534,12 @@ static void parse_options(struct ts *ts, int argc, char **argv) {
499 534
 				ts_LOGf("Out filter : Pass through TDT/TOT.\n");
500 535
 		}
501 536
 	}
502
-	ts_LOGf("Server addr: tcp://%s:%u/\n", inet_ntoa(ts->camd.server_addr), ts->camd.server_port);
503
-	ts_LOGf("Server user: %s\n", ts->camd.user);
504
-	ts_LOGf("Server pass: %s\n", ts->camd.pass);
537
+	ts_LOGf("CAMD proto : %s\n", ts->camd.ops.ident);
538
+	ts_LOGf("CAMD addr  : tcp://%s:%u/\n", inet_ntoa(ts->camd.server_addr), ts->camd.server_port);
539
+	ts_LOGf("CAMD user  : %s\n", ts->camd.user);
540
+	ts_LOGf("CAMD pass  : %s\n", ts->camd.pass);
541
+	if (ts->camd.ops.proto == CAMD_NEWCAMD)
542
+		ts_LOGf("CAMD deskey: %s\n", ts->camd.newcamd.hex_des_key);
505 543
 
506 544
 	ts_LOGf("TS discont : %s\n", ts->ts_discont ? "report" : "ignore");
507 545
 	ts->threaded = !(ts->input.type == FILE_IO && ts->input.fd != 0);

+ 19
- 0
util.c View File

@@ -135,3 +135,22 @@ void set_thread_name(char *thread_name) {
135 135
 }
136 136
 
137 137
 #endif
138
+
139
+static int decode_hex_char(char c) {
140
+	if ((c >= '0') && (c <= '9')) return c - '0';
141
+	if ((c >= 'A') && (c <= 'F')) return c - 'A' + 10;
142
+	if ((c >= 'a') && (c <= 'f')) return c - 'a' + 10;
143
+	return -1;
144
+}
145
+
146
+int decode_hex_string(char *hex, uint8_t *bin, int asc_len) {
147
+	int i;
148
+	for (i = 0; i < asc_len; i += 2) {
149
+		int n1 = decode_hex_char(hex[i + 0]);
150
+		int n2 = decode_hex_char(hex[i + 1]);
151
+		if (n1 == -1 || n2 == -1)
152
+			return -1;
153
+		bin[i / 2] = (n1 << 4) | (n2 & 0xf);
154
+	}
155
+	return asc_len / 2;
156
+}

+ 1
- 0
util.h View File

@@ -26,5 +26,6 @@ uint8_t *init_4b(uint32_t val, uint8_t *b);
26 26
 uint8_t *init_4l(uint32_t val, uint8_t *b);
27 27
 uint8_t *init_2b(uint32_t val, uint8_t *b);
28 28
 void set_thread_name(char *thread_name);
29
+int decode_hex_string(char *hex, uint8_t *bin, int asc_len);
29 30
 
30 31
 #endif

Loading…
Cancel
Save