aacs.c 20.8 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 {
48
    uint8_t  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 52 53 54

    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
55 56 57

    char    *path;
    int      mkb_version;
npzacs's avatar
npzacs committed
58 59
};

60 61 62
static const uint8_t empty_key[] = "\x00\x00\x00\x00\x00\x00\x00\x00"
                                   "\x00\x00\x00\x00\x00\x00\x00\x00";

63 64
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
65
                        uint8_t *mk)
cRTrn13's avatar
cRTrn13 committed
66
{
67
    gcry_cipher_hd_t gcry_h;
npzacs's avatar
npzacs committed
68
    int a;
cRTrn13's avatar
cRTrn13 committed
69
    uint8_t dec_vd[16];
70
    char str[40];
cRTrn13's avatar
cRTrn13 committed
71

72
    DEBUG(DBG_AACS, "Validate processing key %s...\n", print_hex(str, pk, 16));
cRTrn13's avatar
cRTrn13 committed
73
    DEBUG(DBG_AACS, " Using:\n");
74 75 76
    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));
77

78 79 80
    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);
81

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

86 87 88
    gcry_cipher_setkey(gcry_h, mk, 16);
    gcry_cipher_decrypt (gcry_h, dec_vd, 16, vd, 16);
    gcry_cipher_close(gcry_h);
89

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

npzacs's avatar
npzacs committed
95
    return AACS_ERROR_NO_PK;
cRTrn13's avatar
cRTrn13 committed
96
}
97

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

106 107
    /* Skip if retrieved from config file */
    if (memcmp(aacs->mk, empty_key, 16))
npzacs's avatar
npzacs committed
108
      return AACS_SUCCESS;
109

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

npzacs's avatar
npzacs committed
112
    if ((mkb = mkb_open(aacs->path))) {
113
        DEBUG(DBG_AACS, "Get UVS...\n");
npzacs's avatar
npzacs committed
114
        aacs->mkb_version = mkb_version(mkb);
115 116 117 118 119 120 121 122
        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
123

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

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

139 140
                        char str[40];
                        DEBUG(DBG_AACS, "Media key: %s\n", print_hex(str, aacs->mk,
gates's avatar
gates committed
141
                                                                     16));
npzacs's avatar
npzacs committed
142
                        return AACS_SUCCESS;
143 144 145
                    }
                }

146
                pkcursor = pkcursor->next;
cRTrn13's avatar
keyfile  
cRTrn13 committed
147
            }
148
        }
cRTrn13's avatar
keyfile  
cRTrn13 committed
149

150 151
        mkb_close(mkb);
        X_FREE(buf);
cRTrn13's avatar
keyfile  
cRTrn13 committed
152

npzacs's avatar
npzacs committed
153
        DEBUG(DBG_AACS | DBG_CRIT, "Error calculating media key. Missing right processing key ?\n");
npzacs's avatar
npzacs committed
154
        return AACS_ERROR_NO_PK;
npzacs's avatar
npzacs committed
155
    }
npzacs's avatar
npzacs committed
156

npzacs's avatar
npzacs committed
157
    DEBUG(DBG_AACS | DBG_CRIT, "Error opening %s/AACS/MKB_RO.inf\n", aacs->path);
npzacs's avatar
npzacs committed
158
    return AACS_ERROR_CORRUPTED_DISC;
cRTrn13's avatar
keyfile  
cRTrn13 committed
159
}
160

npzacs's avatar
npzacs committed
161
static int _read_vid(AACS *aacs)
cRTrn13's avatar
openssl  
cRTrn13 committed
162
{
163 164
    /* Use VID given in config file if available */
    if (memcmp(aacs->vid, empty_key, 16)) {
npzacs's avatar
npzacs committed
165
        return AACS_SUCCESS;
166 167
    }

cRTrn13's avatar
cRTrn13 committed
168
    MMC* mmc = NULL;
npzacs's avatar
npzacs committed
169
    if (!(mmc = mmc_open(aacs->path))) {
npzacs's avatar
npzacs committed
170
        return AACS_ERROR_MMC_OPEN;
171 172
    }

npzacs's avatar
npzacs committed
173 174
    int error_code = AACS_ERROR_NO_CERT;

175 176 177 178 179 180 181 182 183
    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)) {
npzacs's avatar
npzacs committed
184 185
            DEBUG(DBG_AACS, "Not using invalid host certificate %s.\n",
                  print_hex(tmp_str, cert, 92));
186

npzacs's avatar
npzacs committed
187 188 189
            hccursor = hccursor->next;
            continue;
        }
190 191 192 193

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

npzacs's avatar
npzacs committed
194
        int mmc_result = mmc_read_vid(mmc, priv_key, cert, aacs->vid);
npzacs's avatar
npzacs committed
195 196 197 198 199 200 201 202 203 204 205
        switch (mmc_result) {
            case MMC_SUCCESS:
                mmc_close(mmc);
                return AACS_SUCCESS;
            case MMC_ERROR_CERT_REVOKED:
                error_code = AACS_ERROR_CERT_REVOKED;
                break;
            case MMC_ERROR:
            default:
                error_code = AACS_ERROR_MMC_FAILURE;
                break;
206 207 208 209 210 211 212 213
        }

        hccursor = hccursor->next;
    }

    mmc_close(mmc);

    DEBUG(DBG_AACS, "Error reading VID!\n");
npzacs's avatar
npzacs committed
214
    return error_code;
215
}
cRTrn13's avatar
openssl  
cRTrn13 committed
216

npzacs's avatar
npzacs committed
217
static int _calc_vuk(AACS *aacs)
218
{
npzacs's avatar
npzacs committed
219 220
    int error_code;

221
    /* Skip if retrieved from config file */
npzacs's avatar
npzacs committed
222 223 224 225
    if (memcmp(aacs->vuk, empty_key, 16)) {
        DEBUG(DBG_AACS, "Using VUK from config file\n");
        return AACS_SUCCESS;
    }
226

npzacs's avatar
npzacs committed
227 228 229
    /* get cached vuk */
    if (keycache_find("vuk", aacs->disc_id, aacs->vuk, 16)) {
        DEBUG(DBG_AACS, "Using cached VUK\n");
npzacs's avatar
npzacs committed
230
        return AACS_SUCCESS;
npzacs's avatar
npzacs committed
231 232
    }

233
    /* make sure we have media key */
npzacs's avatar
npzacs committed
234
    error_code = _calc_mk(aacs);
npzacs's avatar
npzacs committed
235 236
    if (error_code != AACS_SUCCESS) {
        return error_code;
237 238
    }

npzacs's avatar
npzacs committed
239
    /* acquire VID */
npzacs's avatar
npzacs committed
240
    error_code = _read_vid(aacs);
npzacs's avatar
npzacs committed
241 242 243
    if (error_code != AACS_SUCCESS) {
        return error_code;
    }
244

npzacs's avatar
npzacs committed
245 246
    int a;
    gcry_cipher_hd_t gcry_h;
247

npzacs's avatar
npzacs committed
248 249 250 251
    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);
252

npzacs's avatar
npzacs committed
253 254 255
    for (a = 0; a < 16; a++) {
        aacs->vuk[a] ^= aacs->vid[a];
    }
256

npzacs's avatar
npzacs committed
257 258
    char str[40];
    DEBUG(DBG_AACS, "Volume unique key: %s\n", print_hex(str, aacs->vuk, 16));
259

npzacs's avatar
npzacs committed
260 261
    /* cache vuk */
    keycache_save("vuk", aacs->disc_id, aacs->vuk, 16);
npzacs's avatar
npzacs committed
262

npzacs's avatar
npzacs committed
263
    return AACS_SUCCESS;
cRTrn13's avatar
openssl  
cRTrn13 committed
264 265
}

266 267
static uint16_t _read_u16(AACS_FILE_H *fp)
{
npzacs's avatar
npzacs committed
268
  uint8_t data[2] = {0, 0};
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 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314

  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
315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330
static AACS_FILE_H *_open_unit_key_file(const char *path)
{
    AACS_FILE_H *fp;
    char        *f_name;

    f_name = str_printf("%s/AACS/Unit_Key_RO.inf", path);
    fp = file_open(f_name, "rb");

    if (!fp) {
        DEBUG(DBG_AACS | DBG_CRIT, "Error opening unit key file %s\n", f_name);
    }

    X_FREE(f_name);
    return fp;
}

npzacs's avatar
npzacs committed
331
static int _calc_uks(AACS *aacs)
cRTrn13's avatar
openssl  
cRTrn13 committed
332
{
333
    AACS_FILE_H *fp = NULL;
334
    uint8_t  buf[16];
335
    uint64_t f_pos;
336
    unsigned int i;
npzacs's avatar
npzacs committed
337
    int error_code;
cRTrn13's avatar
openssl  
cRTrn13 committed
338

339 340
    /* Skip if retrieved from config file */
    if (aacs->uks)
npzacs's avatar
npzacs committed
341
        return AACS_SUCCESS;
342

343
    /* Make sure we have VUK */
npzacs's avatar
npzacs committed
344
    error_code = _calc_vuk(aacs);
npzacs's avatar
npzacs committed
345 346
    if (error_code != AACS_SUCCESS) {
        return error_code;
347
    }
348

349 350
    DEBUG(DBG_AACS, "Calculate CPS unit keys...\n");

npzacs's avatar
npzacs committed
351
    fp = _open_unit_key_file(aacs->path);
npzacs's avatar
npzacs committed
352 353 354
    if (!fp) {
        return AACS_ERROR_CORRUPTED_DISC;
    }
cRTrn13's avatar
openssl  
cRTrn13 committed
355

npzacs's avatar
npzacs committed
356 357
    if ((file_read(fp, buf, 4)) == 4) {
        f_pos = MKINT_BE32(buf);
npzacs's avatar
npzacs committed
358

npzacs's avatar
npzacs committed
359 360 361 362
        // Read number of keys
        file_seek(fp, f_pos, SEEK_SET);
        if ((file_read(fp, buf, 2)) == 2) {
            aacs->num_uks = MKINT_BE16(buf);
npzacs's avatar
npzacs committed
363

npzacs's avatar
npzacs committed
364 365
            X_FREE(aacs->uks);
            aacs->uks = calloc(aacs->num_uks, 16);
npzacs's avatar
npzacs committed
366

npzacs's avatar
npzacs committed
367
            DEBUG(DBG_AACS, "%d CPS unit keys\n", aacs->num_uks);
npzacs's avatar
npzacs committed
368

npzacs's avatar
npzacs committed
369 370 371 372 373
        } else {
            aacs->num_uks = 0;
            DEBUG(DBG_AACS | DBG_CRIT, "Error reading number of unit keys\n");
            error_code = AACS_ERROR_CORRUPTED_DISC;
        }
cRTrn13's avatar
openssl  
cRTrn13 committed
374

npzacs's avatar
npzacs committed
375 376 377
        // Read keys
        for (i = 0; i < aacs->num_uks; i++) {
            f_pos += 48;
cRTrn13's avatar
openssl  
cRTrn13 committed
378

npzacs's avatar
npzacs committed
379 380 381 382 383 384
            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;
                error_code = AACS_ERROR_CORRUPTED_DISC;
                break;
npzacs's avatar
npzacs committed
385
            }
cRTrn13's avatar
openssl  
cRTrn13 committed
386

npzacs's avatar
npzacs committed
387 388 389 390 391 392
            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
393

npzacs's avatar
npzacs committed
394 395 396
            char str[40];
            DEBUG(DBG_AACS, "Unit key %d: %s\n", i,
                  print_hex(str, aacs->uks + 16*i, 16));
397 398
        }

npzacs's avatar
npzacs committed
399 400 401
        /* failing next is not fatal, it just slows down things */
        _read_uks_map(aacs, fp);

402
        file_close(fp);
cRTrn13's avatar
openssl  
cRTrn13 committed
403

npzacs's avatar
npzacs committed
404 405 406 407 408 409 410
        return error_code;
    }

    file_close(fp);

    DEBUG(DBG_AACS | DBG_CRIT, "Error reading unit keys\n");
    return AACS_ERROR_CORRUPTED_DISC;
cRTrn13's avatar
openssl  
cRTrn13 committed
411 412
}

413 414
static int _calc_title_hash(const char *path, uint8_t *title_hash)
{
415
    AACS_FILE_H *fp = NULL;
416 417 418
    uint8_t *ukf_buf;
    char     str[48];
    int64_t  f_size;
npzacs's avatar
npzacs committed
419
    int      result = AACS_SUCCESS;
420

npzacs's avatar
npzacs committed
421 422 423
    fp = _open_unit_key_file(path);
    if (!fp) {
        return AACS_ERROR_CORRUPTED_DISC;
424 425 426 427 428 429 430 431
    }

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

    ukf_buf = malloc(f_size);

npzacs's avatar
npzacs committed
432 433 434
    if ((file_read(fp, ukf_buf, f_size)) == f_size) {
        crypto_aacs_title_hash(ukf_buf, f_size, title_hash);
        DEBUG(DBG_AACS, "Disc ID: %s\n", print_hex(str, title_hash, 20));
cRTrn13's avatar
cRTrn13 committed
435

npzacs's avatar
npzacs committed
436 437 438
    } else {
        result = AACS_ERROR_CORRUPTED_DISC;
        DEBUG(DBG_AACS | DBG_CRIT, "Failed to read %"PRIu64" bytes from unit key file %s/AACS/Unit_Key_RO.inf", f_size, path);
439 440 441 442 443
    }

    file_close(fp);
    X_FREE(ukf_buf);

npzacs's avatar
npzacs committed
444
    return result;
445
}
cRTrn13's avatar
cRTrn13 committed
446

npzacs's avatar
npzacs committed
447
static int _verify_ts(uint8_t *buf, size_t size)
448
{
449 450
    uint8_t *ptr;

cRTrn13's avatar
cRTrn13 committed
451 452 453 454 455 456 457
    if (size < 192) {
        return 1;
    }

    for (ptr=buf; ptr < buf+192; ptr++) {
        int failed = 0;
        if (*ptr == 0x47) {
458 459 460 461
            uint8_t *ptr2;

            for (ptr2=ptr; ptr2 < buf + size; ptr2 += 192) {
                if (*ptr2 != 0x47) {
cRTrn13's avatar
cRTrn13 committed
462 463
                    failed = 1;
                    break;
464 465
                }
            }
cRTrn13's avatar
cRTrn13 committed
466 467 468
            if (!failed) {
                return 1;
            }
469 470 471 472
        }
        ptr++;
    }

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

cRTrn13's avatar
cRTrn13 committed
475
    return 0;
476 477
}

478
/* Function that collects keys from keydb config entry */
479
static void _find_config_entry(AACS *aacs)
480
{
npzacs's avatar
npzacs committed
481
    uint8_t discid[20];
482
    char str[48];
npzacs's avatar
npzacs committed
483

484 485
    aacs->uks = NULL;
    aacs->num_uks = 0;
cRTrn13's avatar
cRTrn13 committed
486

npzacs's avatar
npzacs committed
487
    if (aacs->cf && aacs->cf->list) {
488 489 490
        struct title_entry_list_t *ce;
        ce = aacs->cf->list;
        while (ce && ce->entry.discid) {
491 492
            memset(discid, 0, sizeof(discid));
            hexstring_to_hex_array(discid, sizeof(discid),
493
                                   ce->entry.discid);
npzacs's avatar
npzacs committed
494
            if (!memcmp(aacs->disc_id, discid, 20)) {
495
                DEBUG(DBG_AACS, "Found config entry for discid %s\n",
496
                      ce->entry.discid);
497
                break;
498 499
            }

500 501 502 503
            ce = ce->next;
        }
        if (!ce) {
            return;
504
        }
505

506
        if (ce->entry.mek) {
507
            hexstring_to_hex_array(aacs->mk, sizeof(aacs->mk),
508
                                   ce->entry.mek);
509 510

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

514
        if (ce->entry.vid) {
515
            hexstring_to_hex_array(aacs->vid, sizeof(aacs->vid),
516
                                    ce->entry.vid);
517 518

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

522
        if (ce->entry.vuk) {
523
            hexstring_to_hex_array(aacs->vuk, sizeof(aacs->vuk),
524
                                    ce->entry.vuk);
525 526

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

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

533
            digit_key_pair_list *ukcursor = ce->entry.uk;
534 535 536 537 538 539 540 541 542 543 544 545 546 547 548
            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;
            }
        }
549
    }
550 551
}

552
#define ALIGNED_UNIT_LEN 6144
553
static int _decrypt_unit(AACS *aacs, uint8_t *out_buf, const uint8_t *in_buf, uint32_t curr_uk)
cRTrn13's avatar
cRTrn13 committed
554
{
555 556 557 558 559 560 561
    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);
562
    gcry_cipher_encrypt(gcry_h, key, 16, in_buf, 16);
563
    gcry_cipher_close(gcry_h);
cRTrn13's avatar
cRTrn13 committed
564

cRTrn13's avatar
cRTrn13 committed
565
    for (a = 0; a < 16; a++) {
566
        key[a] ^= in_buf[a];
cRTrn13's avatar
cRTrn13 committed
567 568
    }

569 570
    memcpy(out_buf, in_buf, 16); /* first 16 bytes are plain */

571 572 573
    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);
574
    gcry_cipher_decrypt(gcry_h, out_buf + 16, ALIGNED_UNIT_LEN - 16, in_buf + 16, ALIGNED_UNIT_LEN - 16);
575
    gcry_cipher_close(gcry_h);
cRTrn13's avatar
cRTrn13 committed
576

577
    if (_verify_ts(out_buf, ALIGNED_UNIT_LEN)) {
cRTrn13's avatar
cRTrn13 committed
578 579 580
        return 1;
    }

581
    if (curr_uk < aacs->num_uks - 1) {
582
        return _decrypt_unit(aacs, out_buf, in_buf, curr_uk++);
npzacs's avatar
npzacs committed
583 584
    }

cRTrn13's avatar
cRTrn13 committed
585 586 587
    return 0;
}

npzacs's avatar
npzacs committed
588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613
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) {
npzacs's avatar
npzacs committed
614
        DEBUG(DBG_AACS | DBG_CRIT, "No valid AACS configuration files found\n");
npzacs's avatar
npzacs committed
615
        return AACS_ERROR_NO_CONFIG;
npzacs's avatar
npzacs committed
616 617
    }

npzacs's avatar
npzacs committed
618
    return AACS_SUCCESS;
npzacs's avatar
npzacs committed
619 620
}

npzacs's avatar
npzacs committed
621 622
void aacs_get_version(int *major, int *minor, int *micro)
{
npzacs's avatar
npzacs committed
623 624 625
    *major = AACS_VERSION_MAJOR;
    *minor = AACS_VERSION_MINOR;
    *micro = AACS_VERSION_MICRO;
npzacs's avatar
npzacs committed
626 627
}

npzacs's avatar
npzacs committed
628
/* aacs_open2() wrapper for backwards compability */
cRTrn13's avatar
cRTrn13 committed
629
AACS *aacs_open(const char *path, const char *configfile_path)
npzacs's avatar
npzacs committed
630 631 632 633 634 635 636 637 638 639 640 641 642 643
{
    int error_code;
    AACS *aacs;

    aacs = aacs_open2(path, configfile_path, &error_code);
    if (error_code == AACS_SUCCESS) {
        return aacs;
    }

    aacs_close(aacs);
    return NULL;
}

AACS *aacs_open2(const char *path, const char *configfile_path, int *error_code)
cRTrn13's avatar
cRTrn13 committed
644
{
npzacs's avatar
npzacs committed
645
    DEBUG(DBG_AACS, "libaacs "AACS_VERSION_STRING" [%zd]\n", sizeof(AACS));
cRTrn13's avatar
cRTrn13 committed
646

647
    DEBUG(DBG_AACS, "Initializing libgcrypt...\n");
npzacs's avatar
npzacs committed
648
    if (!crypto_init()) {
npzacs's avatar
npzacs committed
649
        DEBUG(DBG_AACS | DBG_CRIT, "Failed to initialize libgcrypt\n");
650 651
        return NULL;
    }
652

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

npzacs's avatar
npzacs committed
655 656
    *error_code = _load_config(aacs, configfile_path);
    if (*error_code != AACS_SUCCESS) {
657 658 659 660
        aacs_close(aacs);
        return NULL;
    }

npzacs's avatar
npzacs committed
661 662
    aacs->path = str_printf("%s", path);

npzacs's avatar
npzacs committed
663 664 665 666 667
    *error_code = _calc_title_hash(path, aacs->disc_id);
    if (*error_code != AACS_SUCCESS) {
        aacs_close(aacs);
        return NULL;
    }
npzacs's avatar
npzacs committed
668

npzacs's avatar
npzacs committed
669 670
    DEBUG(DBG_AACS, "Searching for keydb config entry...\n");
    _find_config_entry(aacs);
671

npzacs's avatar
npzacs committed
672
    DEBUG(DBG_AACS, "Starting AACS waterfall...\n");
npzacs's avatar
npzacs committed
673
    *error_code = _calc_uks(aacs);
cRTrn13's avatar
cRTrn13 committed
674

npzacs's avatar
npzacs committed
675 676
    keydbcfg_config_file_close(aacs->cf);
    aacs->cf = NULL;
cRTrn13's avatar
cRTrn13 committed
677

npzacs's avatar
npzacs committed
678 679 680 681 682
    if (*error_code == AACS_SUCCESS) {
        DEBUG(DBG_AACS, "AACS initialized! (%p)\n", aacs);
    } else {
        DEBUG(DBG_AACS, "Failed to initialize AACS! (%p)\n", aacs);
    }
683

npzacs's avatar
npzacs committed
684
    return aacs;
cRTrn13's avatar
cRTrn13 committed
685 686
}

cRTrn13's avatar
cRTrn13 committed
687
void aacs_close(AACS *aacs)
cRTrn13's avatar
cRTrn13 committed
688
{
npzacs's avatar
npzacs committed
689 690 691
    if (!aacs)
        return;

npzacs's avatar
npzacs committed
692 693 694 695 696
    if (aacs->cf) {
        keydbcfg_config_file_close(aacs->cf);
        aacs->cf = NULL;
    }

697
    X_FREE(aacs->uks);
698
    X_FREE(aacs->cps_units);
npzacs's avatar
npzacs committed
699
    X_FREE(aacs->path);
700

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

cRTrn13's avatar
cRTrn13 committed
703 704 705
    X_FREE(aacs);
}

706
int aacs_decrypt_unit(AACS *aacs, uint8_t *buf)
707
{
708 709
    uint8_t out_buf[ALIGNED_UNIT_LEN];

710 711 712 713 714
    if (!(buf[0] & 0xc0)) {
        // TP_extra_header Copy_permission_indicator == 0, unit is not encrypted
        return 1;
    }

715
    if (_decrypt_unit(aacs, out_buf, buf, aacs->current_cps_unit)) {
716
        memcpy(buf, out_buf, ALIGNED_UNIT_LEN);
717 718 719 720 721 722 723

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

724 725 726
        return 1;
    }

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

729
    return 0;
730
}
cRTrn13's avatar
cRTrn13 committed
731

npzacs's avatar
npzacs committed
732 733 734 735 736 737 738 739 740 741 742 743
int aacs_get_mkb_version(AACS *aacs)
{
    if (!aacs->mkb_version) {
        MKB *mkb;
        if ((mkb = mkb_open(aacs->path))) {
            aacs->mkb_version = mkb_version(mkb);
            mkb_close(mkb);
        }
    }
    return aacs->mkb_version;
}

npzacs's avatar
npzacs committed
744 745 746 747 748
const uint8_t *aacs_get_disc_id(AACS *aacs)
{
    return aacs->disc_id;
}

749
const uint8_t *aacs_get_vid(AACS *aacs)
cRTrn13's avatar
cRTrn13 committed
750 751 752
{
    return aacs->vid;
}
753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779

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);
}