aacs.c 19.1 KB
Newer Older
1
2
/*
 * This file is part of libaacs
3
 * Copyright (C) 2009-2010  Obliter0n
npzacs's avatar
npzacs committed
4
 * Copyright (C) 2009-2010  npzacs
5
 *
gates's avatar
gates committed
6
7
8
9
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
10
 *
gates's avatar
gates committed
11
 * This library is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
gates's avatar
gates committed
13
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
15
 *
gates's avatar
gates committed
16
17
18
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library. If not, see
 * <http://www.gnu.org/licenses/>.
19
20
 */

npzacs's avatar
npzacs committed
21
22
23
24
#if HAVE_CONFIG_H
#include "config.h"
#endif

npzacs's avatar
npzacs committed
25
26
#include <util/attributes.h>

npzacs's avatar
npzacs committed
27
#include "aacs-version.h"
cRTrn13's avatar
cRTrn13 committed
28
#include "aacs.h"
29
#include "crypto.h"
cRTrn13's avatar
cRTrn13 committed
30
#include "mmc.h"
31
#include "mkb.h"
gates's avatar
gates committed
32
#include "file/file.h"
gates's avatar
gates committed
33
#include "file/keydbcfg.h"
34
35
#include "util/macro.h"
#include "util/logging.h"
36
#include "util/strutl.h"
cRTrn13's avatar
cRTrn13 committed
37

38
#include <inttypes.h>
39
#include <string.h>
40
#include <stdio.h>
npzacs's avatar
npzacs committed
41
42
43
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
44
#include <gcrypt.h>
45

npzacs's avatar
npzacs committed
46

npzacs's avatar
npzacs committed
47
struct aacs {
npzacs's avatar
npzacs committed
48
    uint8_t pk[16], mk[16], vuk[16], vid[16], disc_id[20], *uks;
npzacs's avatar
npzacs committed
49
50
    uint32_t num_uks;
    struct config_file_t *cf;
51
    struct title_entry_list_t *ce;
52
53
54
55

    uint32_t num_titles;
    uint16_t current_cps_unit;
    uint16_t *cps_units;  /* [0] = first play ; [1] = top menu ; [2] = title 1 ... */
npzacs's avatar
npzacs committed
56
57
};

58
59
60
static const uint8_t empty_key[] = "\x00\x00\x00\x00\x00\x00\x00\x00"
                                   "\x00\x00\x00\x00\x00\x00\x00\x00";

61
62
static int _validate_pk(const uint8_t *pk,
                        const uint8_t *cvalue, const uint8_t *uv, const uint8_t *vd,
npzacs's avatar
npzacs committed
63
                        uint8_t *mk)
cRTrn13's avatar
cRTrn13 committed
64
{
65
66
    gcry_cipher_hd_t gcry_h;
    int a, ret = 0;
cRTrn13's avatar
cRTrn13 committed
67
    uint8_t dec_vd[16];
68
    char str[40];
cRTrn13's avatar
cRTrn13 committed
69

70
    DEBUG(DBG_AACS, "Validate processing key %s...\n", print_hex(str, pk, 16));
cRTrn13's avatar
cRTrn13 committed
71
    DEBUG(DBG_AACS, " Using:\n");
72
73
74
    DEBUG(DBG_AACS, "   UV: %s\n", print_hex(str, uv, 4));
    DEBUG(DBG_AACS, "   cvalue: %s\n", print_hex(str, cvalue, 16));
    DEBUG(DBG_AACS, "   Verification data: %s\n", print_hex(str, vd, 16));
75

76
77
78
    gcry_cipher_open(&gcry_h, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_ECB, 0);
    gcry_cipher_setkey(gcry_h, pk, 16);
    gcry_cipher_decrypt(gcry_h, mk, 16, cvalue, 16);
79

cRTrn13's avatar
cRTrn13 committed
80
81
82
83
    for (a = 0; a < 4; a++) {
        mk[a + 12] ^= uv[a];
    }

84
85
86
    gcry_cipher_setkey(gcry_h, mk, 16);
    gcry_cipher_decrypt (gcry_h, dec_vd, 16, vd, 16);
    gcry_cipher_close(gcry_h);
87

cRTrn13's avatar
cRTrn13 committed
88
    if (!memcmp(dec_vd, "\x01\x23\x45\x67\x89\xAB\xCD\xEF", 8)) {
npzacs's avatar
npzacs committed
89
        DEBUG(DBG_AACS, "Processing key %s is valid!\n", print_hex(str, pk, 16));
90
        ret = 1;
cRTrn13's avatar
cRTrn13 committed
91
92
    }

93
    return ret;
cRTrn13's avatar
cRTrn13 committed
94
}
95

npzacs's avatar
npzacs committed
96
static int _calc_mk(AACS *aacs, const char *path)
cRTrn13's avatar
keyfile    
cRTrn13 committed
97
98
99
{
    int a, num_uvs = 0;
    size_t len;
100
    uint8_t *buf = NULL;
cRTrn13's avatar
keyfile    
cRTrn13 committed
101
    MKB *mkb = NULL;
102
    const uint8_t *rec, *uvs;
cRTrn13's avatar
keyfile    
cRTrn13 committed
103

104
105
106
107
    /* Skip if retrieved from config file */
    if (memcmp(aacs->mk, empty_key, 16))
      return 1;

cRTrn13's avatar
cRTrn13 committed
108
109
    DEBUG(DBG_AACS, "Calculate media key...\n");

110
111
112
113
114
115
116
117
118
119
    if ((mkb = mkb_open(path))) {
        DEBUG(DBG_AACS, "Get UVS...\n");
        uvs = mkb_subdiff_records(mkb, &len);
        rec = uvs;
        while (rec < uvs + len) {
            if (rec[0] & 0xc0)
                break;
            rec += 5;
            num_uvs++;
        }
cRTrn13's avatar
keyfile    
cRTrn13 committed
120

121
122
        DEBUG(DBG_AACS, "Get cvalues...\n");
        rec = mkb_cvalues(mkb, &len);
123
124
125
126
127
        if (aacs->cf->pkl) {
            pk_list *pkcursor = aacs->cf->pkl;
            while (pkcursor && pkcursor->key) {
                hexstring_to_hex_array(aacs->pk, sizeof(aacs->pk),
                                       pkcursor->key);
128
129
130
                DEBUG(DBG_AACS, "Trying processing key...\n");

                for (a = 0; a < num_uvs; a++) {
gates's avatar
gates committed
131
132
                    if (_validate_pk(aacs->pk, rec + a * 16, uvs + 1 + a * 5,
                      mkb_mk_dv(mkb), aacs->mk)) {
133
134
                        mkb_close(mkb);
                        X_FREE(buf);
135

136
137
                        char str[40];
                        DEBUG(DBG_AACS, "Media key: %s\n", print_hex(str, aacs->mk,
gates's avatar
gates committed
138
                                                                     16));
139
140
141
142
                        return 1;
                    }
                }

143
                pkcursor = pkcursor->next;
cRTrn13's avatar
keyfile    
cRTrn13 committed
144
            }
145
        }
cRTrn13's avatar
keyfile    
cRTrn13 committed
146

147
148
        mkb_close(mkb);
        X_FREE(buf);
cRTrn13's avatar
keyfile    
cRTrn13 committed
149
150
    }

npzacs's avatar
npzacs committed
151
152
    DEBUG(DBG_AACS, "Error calculating media key!\n");

cRTrn13's avatar
keyfile    
cRTrn13 committed
153
154
    return 0;
}
155

156
static int _read_vid(AACS *aacs, const char *path)
cRTrn13's avatar
openssl    
cRTrn13 committed
157
{
158
159
160
161
162
    /* Use VID given in config file if available */
    if (memcmp(aacs->vid, empty_key, 16)) {
        return 1;
    }

cRTrn13's avatar
cRTrn13 committed
163
    MMC* mmc = NULL;
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
    if (!(mmc = mmc_open(path))) {
        return 0;
    }

    cert_list *hccursor = aacs->cf->host_cert_list;
    while (hccursor && hccursor->host_priv_key && hccursor->host_cert) {

        char tmp_str[2*92+1];
        uint8_t priv_key[20], cert[92];
        hexstring_to_hex_array(priv_key, sizeof(priv_key), hccursor->host_priv_key);
        hexstring_to_hex_array(cert,     sizeof(cert),     hccursor->host_cert);

        if (!crypto_aacs_verify_host_cert(cert)) {
	    DEBUG(DBG_AACS, "Not using invalid host certificate %s.\n",
		  print_hex(tmp_str, cert, 92));

	    hccursor = hccursor->next;
	    continue;
	}

        DEBUG(DBG_AACS, "Trying host certificate (id 0x%s)...\n",
              print_hex(tmp_str, cert + 4, 6));

npzacs's avatar
npzacs committed
187
188
        int mmc_result = mmc_read_vid(mmc, priv_key, cert, aacs->vid);
        if (mmc_result == MMC_SUCCESS) {
189
190
191
192
193
194
195
196
197
198
199
200
            mmc_close(mmc);
            return 1;
        }

        hccursor = hccursor->next;
    }

    mmc_close(mmc);

    DEBUG(DBG_AACS, "Error reading VID!\n");
    return 0;
}
cRTrn13's avatar
openssl    
cRTrn13 committed
201

202
203
static int _calc_vuk(AACS *aacs, const char *path)
{
204
205
206
207
    /* Skip if retrieved from config file */
    if (memcmp(aacs->vuk, empty_key, 16))
      return 1;

npzacs's avatar
npzacs committed
208
209
210
211
212
213
    /* get cached vuk */
    if (keycache_find("vuk", aacs->disc_id, aacs->vuk, 16)) {
        DEBUG(DBG_AACS, "Using cached VUK\n");
        return 1;
    }

214
215
216
217
218
    /* make sure we have media key */
    if (!_calc_mk(aacs, path)) {
        return 0;
    }

219
220
    DEBUG(DBG_AACS, "Calculate volume unique key...\n");

221
222
223
    if (_read_vid(aacs, path)) {

        int a;
224
        gcry_cipher_hd_t gcry_h;
225

226
227
228
229
230
231
232
233
234
235
        gcry_cipher_open(&gcry_h, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_ECB, 0);
        gcry_cipher_setkey(gcry_h, aacs->mk, 16);
        gcry_cipher_decrypt(gcry_h, aacs->vuk, 16, aacs->vid, 16);
        gcry_cipher_close(gcry_h);

        for (a = 0; a < 16; a++) {
            aacs->vuk[a] ^= aacs->vid[a];
        }

        char str[40];
236
        DEBUG(DBG_AACS, "Volume unique key: %s\n", print_hex(str, aacs->vuk, 16));
237

npzacs's avatar
npzacs committed
238
239
240
        /* cache vuk */
        keycache_save("vuk", aacs->disc_id, aacs->vuk, 16);

241
242
243
        return 1;
    }

244
245
    DEBUG(DBG_AACS, "Error calculating VUK!\n");

cRTrn13's avatar
openssl    
cRTrn13 committed
246
247
248
    return 0;
}

249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
static uint16_t _read_u16(AACS_FILE_H *fp)
{
  uint8_t data[2];

  file_read(fp, data, sizeof(uint16_t));

  return MKINT_BE16(data);
}

static void _read_uks_map(AACS *aacs, AACS_FILE_H *fp)
{
    uint16_t first_play, top_menu;
    unsigned i;

    DEBUG(DBG_AACS, "Assigning CPS units to titles ...\n");

    X_FREE(aacs->cps_units);
    aacs->current_cps_unit = 0;

    file_seek(fp, 16 + 4, SEEK_SET);

    first_play = _read_u16(fp);
    top_menu   = _read_u16(fp);

    DEBUG(DBG_AACS, "Title FP : CPS unit %d\n", first_play);
    DEBUG(DBG_AACS, "Title TM : CPS unit %d\n", top_menu);

    aacs->num_titles   = _read_u16(fp);
    aacs->cps_units    = calloc(sizeof(uint16_t), aacs->num_titles + 2);
    aacs->cps_units[0] = first_play;
    aacs->cps_units[1] = top_menu;

    for (i = 2; i < aacs->num_titles + 2; i++) {
        _read_u16(fp); /* reserved */
        aacs->cps_units[i] = _read_u16(fp);
        DEBUG(DBG_AACS, "Title %02d : CPS unit %d\n", i - 1, aacs->cps_units[i]);
    }

    /* validate */
    for (i = 0; i < aacs->num_titles + 2; i++) {
        if (aacs->cps_units[i])
            aacs->cps_units[i]--; /* number [1...N] --> index [0...N-1] */
        if (aacs->cps_units[i] >= aacs->num_uks) {
            DEBUG(DBG_AACS, " *** Invalid CPS unit for title %d: %d !\n", (int) i - 1, aacs->cps_units[i]);
            aacs->cps_units[i] = 0;
        }
    }
}

npzacs's avatar
npzacs committed
298
static int _calc_uks(AACS *aacs, const char *path)
cRTrn13's avatar
openssl    
cRTrn13 committed
299
{
300
    AACS_FILE_H *fp = NULL;
301
302
    char    *f_name;
    uint8_t  buf[16];
303
    uint64_t f_pos;
304
    unsigned int i;
cRTrn13's avatar
openssl    
cRTrn13 committed
305

306
307
    /* Skip if retrieved from config file */
    if (aacs->uks)
308
        return 1;
309

310
311
    /* Make sure we have VUK */
    if (!_calc_vuk(aacs, path)) {
312
        return 0;
313
    }
314

315
316
    DEBUG(DBG_AACS, "Calculate CPS unit keys...\n");

npzacs's avatar
npzacs committed
317
    f_name = str_printf("%s/AACS/Unit_Key_RO.inf", path);
318
319
    fp = file_open(f_name, "rb");
    X_FREE(f_name);
cRTrn13's avatar
openssl    
cRTrn13 committed
320

321
    if (fp) {
322
        if ((file_read(fp, buf, 4)) == 4) {
npzacs's avatar
npzacs committed
323
            f_pos = MKINT_BE32(buf);
cRTrn13's avatar
openssl    
cRTrn13 committed
324

npzacs's avatar
npzacs committed
325
            // Read number of keys
cRTrn13's avatar
cRTrn13 committed
326
            file_seek(fp, f_pos, SEEK_SET);
npzacs's avatar
npzacs committed
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
            if ((file_read(fp, buf, 2)) == 2) {
                aacs->num_uks = MKINT_BE16(buf);

                X_FREE(aacs->uks);
                aacs->uks = calloc(aacs->num_uks, 16);

                DEBUG(DBG_AACS, "%d CPS unit keys\n", aacs->num_uks);

            } else {
                aacs->num_uks = 0;
                DEBUG(DBG_AACS, "Error reading number of unit keys!\n");
            }

            // Read keys
            for (i = 0; i < aacs->num_uks; i++) {
                f_pos += 48;

                file_seek(fp, f_pos, SEEK_SET);
                if ((file_read(fp, buf, 16)) != 16) {
                    DEBUG(DBG_AACS, "Unit key %d: read error\n", i);
                    aacs->num_uks = i;
                    break;
                }
cRTrn13's avatar
openssl    
cRTrn13 committed
350

351
352
353
354
355
356
                gcry_cipher_hd_t gcry_h;
                gcry_cipher_open(&gcry_h, GCRY_CIPHER_AES,
                                 GCRY_CIPHER_MODE_ECB, 0);
                gcry_cipher_setkey(gcry_h, aacs->vuk, 16);
                gcry_cipher_decrypt(gcry_h, aacs->uks + 16*i, 16, buf, 16);
                gcry_cipher_close(gcry_h);
cRTrn13's avatar
openssl    
cRTrn13 committed
357

358
                char str[40];
gates's avatar
gates committed
359
                DEBUG(DBG_AACS, "Unit key %d: %s\n", i,
360
                      print_hex(str, aacs->uks + 16*i, 16));
npzacs's avatar
npzacs committed
361
            }
cRTrn13's avatar
openssl    
cRTrn13 committed
362

363
364
            _read_uks_map(aacs, fp);

npzacs's avatar
npzacs committed
365
            file_close(fp);
cRTrn13's avatar
openssl    
cRTrn13 committed
366

npzacs's avatar
npzacs committed
367
            return aacs->num_uks;
368
369
370
        }

        file_close(fp);
cRTrn13's avatar
openssl    
cRTrn13 committed
371
372
    }

373
374
    DEBUG(DBG_AACS, "Could not calculate unit keys!\n");

cRTrn13's avatar
openssl    
cRTrn13 committed
375
376
377
    return 0;
}

378
379
static int _calc_title_hash(const char *path, uint8_t *title_hash)
{
380
    AACS_FILE_H *fp = NULL;
381
382
383
384
385
    uint8_t *ukf_buf;
    char     str[48];
    int64_t  f_size;
    char    *f_name;

npzacs's avatar
npzacs committed
386
    f_name = str_printf("%s/AACS/Unit_Key_RO.inf", path);
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402

    if (!(fp = file_open(f_name, "rb"))) {
        DEBUG(DBG_AACS, "Failed to open unit key file: %s!\n", f_name);
        X_FREE(f_name);
        return 0;
    }

    X_FREE(f_name);

    file_seek(fp, 0, SEEK_END);
    f_size = file_tell(fp);
    file_seek(fp, 0, SEEK_SET);

    ukf_buf = malloc(f_size);

    if ((file_read(fp, ukf_buf, f_size)) != f_size) {
cRTrn13's avatar
cRTrn13 committed
403

404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
        DEBUG(DBG_AACS, "Failed to read %"PRIu64" bytes from unit key file!\n", f_size);

        file_close(fp);
        X_FREE(ukf_buf);

        return 0;
    }

    crypto_aacs_title_hash(ukf_buf, f_size, title_hash);
    DEBUG(DBG_AACS, "Disc ID: %s\n", print_hex(str, title_hash, 20));

    file_close(fp);
    X_FREE(ukf_buf);

    return 1;
}
cRTrn13's avatar
cRTrn13 committed
420

npzacs's avatar
npzacs committed
421
static int _verify_ts(uint8_t *buf, size_t size)
422
{
423
424
    uint8_t *ptr;

cRTrn13's avatar
cRTrn13 committed
425
426
427
428
429
430
431
    if (size < 192) {
        return 1;
    }

    for (ptr=buf; ptr < buf+192; ptr++) {
        int failed = 0;
        if (*ptr == 0x47) {
432
433
434
435
            uint8_t *ptr2;

            for (ptr2=ptr; ptr2 < buf + size; ptr2 += 192) {
                if (*ptr2 != 0x47) {
cRTrn13's avatar
cRTrn13 committed
436
437
                    failed = 1;
                    break;
438
439
                }
            }
cRTrn13's avatar
cRTrn13 committed
440
441
442
            if (!failed) {
                return 1;
            }
443
444
445
446
        }
        ptr++;
    }

cRTrn13's avatar
cRTrn13 committed
447
    DEBUG(DBG_AACS, "Failed to verify TS!\n");
448

cRTrn13's avatar
cRTrn13 committed
449
    return 0;
450
451
}

452
/* Function that collects keys from keydb config entry */
npzacs's avatar
npzacs committed
453
static uint32_t _find_config_entry(AACS *aacs, const char *path)
454
{
npzacs's avatar
npzacs committed
455
    uint8_t discid[20];
456
    char str[48];
457
458
459
    uint32_t retval = 0;
    aacs->uks = NULL;
    aacs->num_uks = 0;
cRTrn13's avatar
cRTrn13 committed
460

npzacs's avatar
npzacs committed
461
    if (!_calc_title_hash(path, aacs->disc_id)) {
cRTrn13's avatar
cRTrn13 committed
462
463
464
        return 0;
    }

npzacs's avatar
npzacs committed
465
    if (aacs->cf && aacs->cf->list) {
466
467
468
469
470
        aacs->ce = aacs->cf->list;
        while (aacs->ce && aacs->ce->entry.discid) {
            memset(discid, 0, sizeof(discid));
            hexstring_to_hex_array(discid, sizeof(discid),
                                   aacs->ce->entry.discid);
npzacs's avatar
npzacs committed
471
            if (!memcmp(aacs->disc_id, discid, 20)) {
472
473
474
                DEBUG(DBG_AACS, "Found config entry for discid %s\n",
                      aacs->ce->entry.discid);
                break;
475
476
            }

477
            aacs->ce = aacs->ce->next;
478
        }
479

480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
        if (aacs->ce->entry.mek) {
            hexstring_to_hex_array(aacs->mk, sizeof(aacs->mk),
                                    aacs->ce->entry.mek);

            DEBUG(DBG_AACS, "Found media key for %s: %s\n",
                  aacs->ce->entry.discid, print_hex(str, aacs->mk, 16));

            retval = 1;
        }

        if (aacs->ce->entry.vid) {
            hexstring_to_hex_array(aacs->vid, sizeof(aacs->vid),
                                    aacs->ce->entry.vid);

            DEBUG(DBG_AACS, "Found volume id for %s: %s\n",
                  aacs->ce->entry.discid, print_hex(str, aacs->vid, 16));

            retval = 1;
        }

500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
        if (aacs->ce->entry.vuk) {
            hexstring_to_hex_array(aacs->vuk, sizeof(aacs->vuk),
                                    aacs->ce->entry.vuk);

            DEBUG(DBG_AACS, "Found volume unique key for %s: %s\n",
                  aacs->ce->entry.discid, print_hex(str, aacs->vuk, 16));

            retval = 1;
        }

        if (aacs->ce && aacs->ce->entry.uk) {
            DEBUG(DBG_AACS, "Acquire CPS unit keys from keydb config file...\n");

            digit_key_pair_list *ukcursor = aacs->ce->entry.uk;
            while (ukcursor && ukcursor->key_pair.key) {
                aacs->num_uks++;

                aacs->uks = (uint8_t*)realloc(aacs->uks, 16 * aacs->num_uks);
                hexstring_to_hex_array(aacs->uks + (16 * (aacs->num_uks - 1)), 16,
                                      ukcursor->key_pair.key);

                char str[40];
                DEBUG(DBG_AACS, "Unit key %d from keydb entry: %s\n",
                      aacs->num_uks,
                      print_hex(str, aacs->uks + (16 * (aacs->num_uks - 1)), 16));

                ukcursor = ukcursor->next;
            }
        }
529
530
    }

531
    if (aacs->num_uks)
532
        retval = aacs->num_uks;
533
534

    return retval;
535
536
}

537
#define ALIGNED_UNIT_LEN 6144
538
static int _decrypt_unit(AACS *aacs, uint8_t *out_buf, const uint8_t *in_buf, uint32_t curr_uk)
cRTrn13's avatar
cRTrn13 committed
539
{
540
541
542
543
544
545
546
    gcry_cipher_hd_t gcry_h;
    int a;
    uint8_t key[16], iv[] = "\x0b\xa0\xf8\xdd\xfe\xa6\x1f\xb3"
                            "\xd8\xdf\x9f\x56\x6a\x05\x0f\x78";

    gcry_cipher_open(&gcry_h, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_ECB, 0);
    gcry_cipher_setkey(gcry_h, aacs->uks + curr_uk * 16, 16);
547
    gcry_cipher_encrypt(gcry_h, key, 16, in_buf, 16);
548
    gcry_cipher_close(gcry_h);
cRTrn13's avatar
cRTrn13 committed
549

cRTrn13's avatar
cRTrn13 committed
550
    for (a = 0; a < 16; a++) {
551
        key[a] ^= in_buf[a];
cRTrn13's avatar
cRTrn13 committed
552
553
    }

554
555
    memcpy(out_buf, in_buf, 16); /* first 16 bytes are plain */

556
557
558
    gcry_cipher_open(&gcry_h, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_CBC, 0);
    gcry_cipher_setkey(gcry_h, key, 16);
    gcry_cipher_setiv(gcry_h, iv, 16);
559
    gcry_cipher_decrypt(gcry_h, out_buf + 16, ALIGNED_UNIT_LEN - 16, in_buf + 16, ALIGNED_UNIT_LEN - 16);
560
    gcry_cipher_close(gcry_h);
cRTrn13's avatar
cRTrn13 committed
561

562
    if (_verify_ts(out_buf, ALIGNED_UNIT_LEN)) {
cRTrn13's avatar
cRTrn13 committed
563
564
565
        return 1;
    }

566
    if (curr_uk < aacs->num_uks - 1) {
567
        return _decrypt_unit(aacs, out_buf, in_buf, curr_uk++);
npzacs's avatar
npzacs committed
568
569
    }

cRTrn13's avatar
cRTrn13 committed
570
571
572
    return 0;
}

npzacs's avatar
npzacs committed
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
static int _load_config(AACS *aacs, const char *configfile_path)
{
    int config_ok = 0;

    aacs->cf = keydbcfg_new_config_file();

    /* try to load KEYDB.cfg */

    if (configfile_path) {
        config_ok = keydbcfg_parse_config(aacs->cf, configfile_path);

    } else {
        /* If no configfile path given, check for config files in user's home or
         * under /etc.
         */
        char *cfgfile = keydbcfg_find_config_file();
        config_ok = keydbcfg_parse_config(aacs->cf, cfgfile);
        X_FREE(cfgfile);
    }

    /* Try to load simple (aacskeys) config files */

    config_ok = keydbcfg_load_pk_file(aacs->cf)   || config_ok;
    config_ok = keydbcfg_load_cert_file(aacs->cf) || config_ok;

    if (!config_ok) {
        DEBUG(DBG_AACS, "No valid configuration files found!\n");
    }

    return config_ok;
}

npzacs's avatar
npzacs committed
605
606
void aacs_get_version(int *major, int *minor, int *micro)
{
npzacs's avatar
npzacs committed
607
608
609
    *major = AACS_VERSION_MAJOR;
    *minor = AACS_VERSION_MINOR;
    *micro = AACS_VERSION_MICRO;
npzacs's avatar
npzacs committed
610
611
}

cRTrn13's avatar
cRTrn13 committed
612
AACS *aacs_open(const char *path, const char *configfile_path)
cRTrn13's avatar
cRTrn13 committed
613
{
npzacs's avatar
npzacs committed
614
    DEBUG(DBG_AACS, "libaacs [%zd]\n", sizeof(AACS));
cRTrn13's avatar
cRTrn13 committed
615

616
617
618
619
620
621
    DEBUG(DBG_AACS, "Initializing libgcrypt...\n");
    if (!crypto_init())
    {
        DEBUG(DBG_AACS, "Failed to initialize libgcrypt\n");
        return NULL;
    }
622

cRTrn13's avatar
cRTrn13 committed
623
    AACS *aacs = calloc(1, sizeof(AACS));
624

npzacs's avatar
npzacs committed
625
    if (_load_config(aacs, configfile_path)) {
626
        DEBUG(DBG_AACS, "Searching for keydb config entry...\n");
npzacs's avatar
npzacs committed
627
        _find_config_entry(aacs, path);
628

629
        DEBUG(DBG_AACS, "Starting AACS waterfall...\n");
630
        if (_calc_uks(aacs, path)) {
npzacs's avatar
npzacs committed
631
632
633
            keydbcfg_config_file_close(aacs->cf);
            aacs->cf = NULL;

634
635
            DEBUG(DBG_AACS, "AACS initialized! (%p)\n", aacs);
            return aacs;
636
        }
637
638
639

        keydbcfg_config_file_close(aacs->cf);
        aacs->cf = NULL;
cRTrn13's avatar
cRTrn13 committed
640
    }
cRTrn13's avatar
cRTrn13 committed
641

npzacs's avatar
npzacs committed
642
    DEBUG(DBG_AACS, "Failed to initialize AACS! (%p)\n", aacs);
cRTrn13's avatar
cRTrn13 committed
643

644
645
    aacs_close(aacs);

cRTrn13's avatar
cRTrn13 committed
646
    return NULL;
cRTrn13's avatar
cRTrn13 committed
647
648
}

cRTrn13's avatar
cRTrn13 committed
649
void aacs_close(AACS *aacs)
cRTrn13's avatar
cRTrn13 committed
650
{
651
    X_FREE(aacs->uks);
652
    X_FREE(aacs->cps_units);
653

npzacs's avatar
npzacs committed
654
    DEBUG(DBG_AACS, "AACS destroyed! (%p)\n", aacs);
cRTrn13's avatar
cRTrn13 committed
655

cRTrn13's avatar
cRTrn13 committed
656
657
658
    X_FREE(aacs);
}

659
int aacs_decrypt_unit(AACS *aacs, uint8_t *buf)
660
{
661
662
    uint8_t out_buf[ALIGNED_UNIT_LEN];

663
664
665
666
667
    if (!(buf[0] & 0xc0)) {
        // TP_extra_header Copy_permission_indicator == 0, unit is not encrypted
        return 1;
    }

668
    if (_decrypt_unit(aacs, out_buf, buf, aacs->current_cps_unit)) {
669
        memcpy(buf, out_buf, ALIGNED_UNIT_LEN);
670
671
672
673
674
675
676

        // Clear copy_permission_indicator bits
        int i;
        for (i = 0; i < 6144; i += 192) {
            buf[i] &= ~0xc0;
        }

677
678
679
        return 1;
    }

680
    DEBUG(DBG_AACS, "Failed decrypting unit [6144 bytes] (%p)\n", aacs);
681

682
    return 0;
683
}
cRTrn13's avatar
cRTrn13 committed
684

npzacs's avatar
npzacs committed
685
686
687
688
689
const uint8_t *aacs_get_disc_id(AACS *aacs)
{
    return aacs->disc_id;
}

690
const uint8_t *aacs_get_vid(AACS *aacs)
cRTrn13's avatar
cRTrn13 committed
691
692
693
{
    return aacs->vid;
}
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720

void aacs_select_title(AACS *aacs, uint32_t title)
{
    if (!aacs) {
        return;
    }

    if (!aacs->cps_units) {
        DEBUG(DBG_AACS|DBG_CRIT, "aacs_select_title(): CPS units not read ! (%p)\n", aacs);
        return;
    }

    if (title == 0xffff) {
        /* first play */
        aacs->current_cps_unit = aacs->cps_units[0];
        DEBUG(DBG_AACS, "aacs_set_title(first_play): CPS unit %d (%p)\n", aacs->current_cps_unit, aacs);
        return;
    }

    if (title <= aacs->num_titles) {
        aacs->current_cps_unit = aacs->cps_units[title + 1];
        DEBUG(DBG_AACS, "aacs_set_title(%d): CPS unit %d (%p)\n", title, aacs->current_cps_unit, aacs);
        return;
    }

    DEBUG(DBG_AACS|DBG_CRIT, "aacs_set_title(%d): invalid title ! (%p)\n", title, aacs);
}