Mercurial > mplayer.hg
comparison stream/stream_bd.c @ 31836:dcd515ac5f6c
Add support for bd:// streams as a test for a part of the AACS algorithm.
Patch by cRTrn13 <crtrn13-at-gmail.com> with some minor fixes by me.
author | reimar |
---|---|
date | Tue, 03 Aug 2010 16:26:50 +0000 |
parents | |
children | af68430bf5de |
comparison
equal
deleted
inserted
replaced
31835:73f85fc599e0 | 31836:dcd515ac5f6c |
---|---|
1 /* | |
2 * Bluray stream playback | |
3 * by cRTrn13 <crtrn13-at-gmail.com> 2009 | |
4 * | |
5 * This file is part of MPlayer. | |
6 * | |
7 * MPlayer is free software; you can redistribute it and/or modify | |
8 * it under the terms of the GNU General Public License as published by | |
9 * the Free Software Foundation; either version 2 of the License, or | |
10 * (at your option) any later version. | |
11 * | |
12 * MPlayer is distributed in the hope that it will be useful, | |
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 * GNU General Public License for more details. | |
16 * | |
17 * You should have received a copy of the GNU General Public License along | |
18 * with MPlayer; if not, write to the Free Software Foundation, Inc., | |
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
20 */ | |
21 | |
22 #include <stdio.h> | |
23 #include <limits.h> | |
24 #include "libavutil/common.h" | |
25 #include "libavutil/aes.h" | |
26 #include "libavutil/sha.h" | |
27 #include "libmpdemux/demuxer.h" | |
28 #include "libavutil/intreadwrite.h" | |
29 #include "m_struct.h" | |
30 #include "m_option.h" | |
31 #include "stream.h" | |
32 | |
33 static const int BD_UNIT_SIZE = 6144; | |
34 static const char *BD_UKF_PATH = "/%s/AACS/Unit_Key_RO.inf"; | |
35 static const char *BD_M2TS_PATH = "/%s/BDMV/STREAM/%05d.m2ts"; | |
36 | |
37 static const char *DEFAULT_BD_DEVICE = "/mnt/bd"; | |
38 | |
39 static const uint8_t BD_CBC_IV[] = { | |
40 0x0b, 0xa0, 0xf8, 0xdd, 0xfe, 0xa6, 0x1f, 0xb3, | |
41 0xd8, 0xdf, 0x9f, 0x56, 0x6a, 0x05, 0x0f, 0x78 | |
42 }; | |
43 | |
44 static const struct stream_priv_s { | |
45 int title; | |
46 char *device; | |
47 } stream_priv_dflts = { | |
48 0, | |
49 NULL | |
50 }; | |
51 | |
52 // Format: bd://[title][</mntlocation>] | |
53 // --> e.g.: bd://117/media/THE_MUMMY/ | |
54 // --> or bd://152 | |
55 // instead of directly, mount location can also be gotten through -dvd-device | |
56 #define ST_OFF(f) M_ST_OFF(struct stream_priv_s,f) | |
57 static const m_option_t stream_opts_fields[] = { | |
58 { "hostname", ST_OFF(title), CONF_TYPE_INT, M_OPT_RANGE, 0, 99999, NULL}, | |
59 { "filename", ST_OFF(device), CONF_TYPE_STRING, 0, 0 ,0, NULL}, | |
60 { NULL, NULL, 0, 0, 0, 0, NULL } | |
61 }; | |
62 | |
63 static const struct m_struct_st stream_opts = { | |
64 "bd", | |
65 sizeof(struct stream_priv_s), | |
66 &stream_priv_dflts, | |
67 stream_opts_fields | |
68 }; | |
69 | |
70 typedef union { | |
71 uint64_t u64[2]; | |
72 uint8_t u8[16]; | |
73 } key; | |
74 | |
75 struct uks { | |
76 int count; | |
77 key *keys; | |
78 }; | |
79 | |
80 struct bd_priv { | |
81 key vuk; | |
82 key iv; | |
83 int title; | |
84 const char *device; | |
85 FILE *title_file; | |
86 struct AVAES *aescbc; | |
87 struct AVAES *aeseed; | |
88 off_t pos; | |
89 struct uks uks; | |
90 }; | |
91 | |
92 static void bd_stream_close(stream_t *s) | |
93 { | |
94 struct bd_priv *bd = s->priv; | |
95 fclose(bd->title_file); | |
96 av_free(bd->aescbc); | |
97 av_free(bd->aeseed); | |
98 free(bd->uks.keys); | |
99 free(bd); | |
100 } | |
101 | |
102 static int bd_stream_seek(stream_t *s, off_t pos) | |
103 { | |
104 struct bd_priv *bd = s->priv; | |
105 | |
106 // must seek to start of unit | |
107 pos -= pos % BD_UNIT_SIZE; | |
108 bd->pos = pos; | |
109 s->pos = pos; | |
110 | |
111 return 1; | |
112 } | |
113 | |
114 static int bd_get_uks(struct bd_priv *bd) | |
115 { | |
116 unsigned char *buf; | |
117 size_t file_size; | |
118 int pos; | |
119 int i, j; | |
120 struct AVAES *a; | |
121 struct AVSHA *asha; | |
122 FILE *file; | |
123 char filename[PATH_MAX]; | |
124 uint8_t discid[20]; | |
125 char *home; | |
126 int vukfound = 0; | |
127 | |
128 snprintf(filename, sizeof(filename), BD_UKF_PATH, bd->device); | |
129 file = fopen(filename, "rb"); | |
130 if (!file) { | |
131 mp_msg(MSGT_OPEN, MSGL_ERR, | |
132 "Cannot open file %s to get UK and DiscID\n", filename); | |
133 return 0; | |
134 } | |
135 fseek(file, 0, SEEK_END); | |
136 file_size = ftell(file); | |
137 if (file_size > 10 * 1024* 1024) { | |
138 mp_msg(MSGT_OPEN, MSGL_ERR, "File %s too large\n", filename); | |
139 fclose(file); | |
140 return 0; | |
141 } | |
142 rewind(file); | |
143 buf = av_malloc(file_size); | |
144 fread(buf, 1, file_size, file); | |
145 fclose(file); | |
146 | |
147 // get discid from file | |
148 asha = av_malloc(av_sha_size); | |
149 av_sha_init(asha, 160); | |
150 av_sha_update(asha, buf, file_size); | |
151 av_sha_final(asha, discid); | |
152 av_free(asha); | |
153 | |
154 // look up discid in KEYDB.cfg to get VUK | |
155 home = getenv("HOME"); | |
156 snprintf(filename, sizeof(filename), "%s/.dvdcss/KEYDB.cfg", home); | |
157 file = fopen(filename, "r"); | |
158 if (!file) { | |
159 mp_msg(MSGT_OPEN,MSGL_ERR, | |
160 "Cannot open VUK database file %s\n", filename); | |
161 av_free(buf); | |
162 return 0; | |
163 } | |
164 while (!feof(file)) { | |
165 char line[1024]; | |
166 uint8_t id[20]; | |
167 char d[200]; | |
168 char *vst; | |
169 unsigned int byte; | |
170 | |
171 fgets(line, sizeof(line), file); | |
172 // file is built up this way: | |
173 // DISCID = title | V | VUK | |
174 // or | |
175 // DISCID = title | key-pair | |
176 // key-pair = V | VUK | |
177 // or D | Date | |
178 // or M | M-key??? | |
179 // or I | I-Key | |
180 // can be followed by ; and comment | |
181 | |
182 //This means: first string up to whitespace is discid | |
183 sscanf(line, "%40s", d); | |
184 for (i = 0; i < 20; ++i) { | |
185 if (sscanf(&d[i*2], "%2x", &byte) != 1) break; | |
186 id[i] = byte; | |
187 } | |
188 if (memcmp(id, discid, 20) != 0) | |
189 continue; | |
190 mp_msg(MSGT_OPEN, MSGL_V, "KeyDB found Entry for DiscID:\n%s\n", line); | |
191 | |
192 vst = strstr(line, "| V |"); | |
193 if (vst == 0) break; | |
194 sscanf(&vst[6], "%32s", d); | |
195 for (i = 0; i < 16; i++) { | |
196 if (sscanf(&d[i*2], "%2x", &byte) != 1) break; | |
197 bd->vuk.u8[i] = byte; | |
198 } | |
199 vukfound = 1; | |
200 } | |
201 fclose(file); | |
202 if (!vukfound) { | |
203 mp_msg(MSGT_OPEN, MSGL_ERR, | |
204 "No Volume Unique Key (VUK) found for this Disc: "); | |
205 for (j = 0; j < 20; j++) mp_msg(MSGT_OPEN, MSGL_ERR, "%02x", discid[j]); | |
206 mp_msg(MSGT_OPEN, MSGL_ERR, "\n"); | |
207 return 0; | |
208 } | |
209 | |
210 pos = AV_RB32(buf); | |
211 if (pos < file_size) { | |
212 int key_pos = pos + 48; | |
213 int max_count = (file_size - key_pos - 16) / 48; | |
214 bd->uks.count = AV_RB16(&buf[pos]); | |
215 if (max_count < bd->uks.count) { | |
216 mp_msg(MSGT_OPEN, MSGL_ERR, | |
217 "File to small for key count %i, using %i\n", | |
218 bd->uks.count, max_count); | |
219 bd->uks.count = max_count; | |
220 } | |
221 bd->uks.keys = calloc(bd->uks.count, sizeof(key)); | |
222 | |
223 a = av_malloc(av_aes_size); | |
224 j = av_aes_init(a, bd->vuk.u8, 128, 1); | |
225 | |
226 mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_BD_DISCID="); | |
227 for (j = 0; j < 20; j++) mp_msg(MSGT_IDENTIFY, MSGL_INFO, "%02x", discid[j]); | |
228 mp_msg(MSGT_IDENTIFY, MSGL_INFO, "\n"); | |
229 mp_msg(MSGT_IDENTIFY, MSGL_INFO, "ID_BD_VUK="); | |
230 for (j = 0; j < 20; j++) mp_msg(MSGT_IDENTIFY, MSGL_INFO, "%02x", discid[j]); | |
231 mp_msg(MSGT_IDENTIFY, MSGL_INFO, "\n"); | |
232 | |
233 for (i = 0; i < bd->uks.count; i++) { | |
234 av_aes_crypt(a, bd->uks.keys[i].u8, &buf[key_pos], 1, NULL, 1); // decrypt unit key | |
235 key_pos += 48; | |
236 } | |
237 | |
238 av_free(a); | |
239 } | |
240 | |
241 av_free(buf); | |
242 return 1; | |
243 } | |
244 | |
245 static off_t bd_read(struct bd_priv *bd, uint8_t *buf, int len) | |
246 { | |
247 int read_len; | |
248 len &= ~15; | |
249 if (!len) | |
250 return 0; | |
251 | |
252 fseek(bd->title_file, bd->pos, SEEK_SET); | |
253 | |
254 read_len = fread(buf, 1, len, bd->title_file); | |
255 if (read_len != len) | |
256 return -1; | |
257 | |
258 if (bd->pos % BD_UNIT_SIZE) { | |
259 // decrypt in place | |
260 av_aes_crypt(bd->aescbc, buf, buf, len / 16, bd->iv.u8, 1); | |
261 } else { | |
262 // reset aes context as at start of unit | |
263 key enc_seed; | |
264 int i; | |
265 memcpy(bd->iv.u8, BD_CBC_IV, sizeof(bd->iv.u8)); | |
266 | |
267 // set up AES key from uk and seed | |
268 av_aes_init(bd->aeseed, bd->uks.keys[0].u8, 128, 0); | |
269 | |
270 // perform encryption of first 16 bytes of unit (seed) | |
271 av_aes_crypt(bd->aeseed, enc_seed.u8, buf, 1, NULL, 0); | |
272 | |
273 // perform xor | |
274 for (i = 0; i < 16; i++) | |
275 enc_seed.u8[i] ^= buf[i]; | |
276 | |
277 // set uk AES-CBC key from enc_seed and BD_CBC_IV | |
278 av_aes_init(bd->aescbc, enc_seed.u8, 128, 1); | |
279 | |
280 // decrypt | |
281 av_aes_crypt(bd->aescbc, &buf[16], &buf[16], (len - 16) / 16, bd->iv.u8, 1); | |
282 } | |
283 | |
284 bd->pos += read_len; | |
285 | |
286 return read_len; | |
287 } | |
288 | |
289 static int bd_stream_fill_buffer(stream_t *s, char *buf, int len) | |
290 { | |
291 int read_len; | |
292 struct bd_priv *bd = s->priv; | |
293 | |
294 read_len = bd_read(bd, buf, len); | |
295 | |
296 s->pos = bd->pos; | |
297 | |
298 return read_len; | |
299 } | |
300 | |
301 static int bd_stream_open(stream_t *s, int mode, void* opts, int* file_format) | |
302 { | |
303 char filename[PATH_MAX]; | |
304 | |
305 struct stream_priv_s* p = opts; | |
306 struct bd_priv *bd = calloc(1, sizeof(*bd)); | |
307 | |
308 if (p->device) | |
309 bd->device = p->device; | |
310 else if (dvd_device) | |
311 bd->device = dvd_device; | |
312 else | |
313 bd->device = DEFAULT_BD_DEVICE; | |
314 | |
315 s->sector_size = BD_UNIT_SIZE; | |
316 s->flags = STREAM_READ | MP_STREAM_SEEK; | |
317 s->fill_buffer = bd_stream_fill_buffer; | |
318 s->seek = bd_stream_seek; | |
319 s->close = bd_stream_close; | |
320 s->start_pos = 0; | |
321 s->priv = bd; | |
322 s->type = STREAMTYPE_BD; | |
323 s->url = strdup("bd://"); | |
324 | |
325 bd->pos = 0; | |
326 bd->title = p->title; | |
327 | |
328 // get and decrypt unit keys | |
329 if (!bd_get_uks(bd)) | |
330 return STREAM_ERROR; | |
331 | |
332 bd->aescbc = av_malloc(av_aes_size); | |
333 bd->aeseed = av_malloc(av_aes_size); | |
334 | |
335 snprintf(filename, sizeof(filename), BD_M2TS_PATH, bd->device, bd->title); | |
336 mp_msg(MSGT_OPEN, MSGL_STATUS, "Opening %s\n", filename); | |
337 bd->title_file = fopen(filename, "rb"); | |
338 if (!bd->title_file) | |
339 return STREAM_ERROR; | |
340 fseek(bd->title_file, 0, SEEK_END); | |
341 s->end_pos = ftell(bd->title_file); | |
342 rewind(bd->title_file); | |
343 | |
344 return STREAM_OK; | |
345 } | |
346 | |
347 const stream_info_t stream_info_bd = { | |
348 "Bluray", | |
349 "bd", | |
350 "cRTrn13", | |
351 "", | |
352 bd_stream_open, | |
353 { "bd", NULL }, | |
354 &stream_opts, | |
355 1 | |
356 }; |