aacs.c 8.54 KB
Newer Older
1 2
/*
 * This file is part of libbluray
npzacs's avatar
npzacs committed
3
 * Copyright (C) 2013-2015  VideoLAN
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
 *
 * 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.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * 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/>.
 */

Petri Hintukainen's avatar
Petri Hintukainen committed
20 21 22 23
#if HAVE_CONFIG_H
#include "config.h"
#endif

Petri Hintukainen's avatar
Petri Hintukainen committed
24
#include "aacs.h"
25

Petri Hintukainen's avatar
Petri Hintukainen committed
26 27
#include "file/dl.h"
#include "file/file.h"
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
#include "util/logging.h"
#include "util/macro.h"
#include "util/strutl.h"

#include <stdlib.h>


struct bd_aacs {
    void           *h_libaacs;   /* library handle from dlopen */
    void           *aacs;        /* aacs handle from aacs_open() */

    const uint8_t *disc_id;
    uint32_t       mkbv;

    /* function pointers */
    fptr_int       decrypt_unit;

    fptr_p_void    get_vid;
    fptr_p_void    get_pmsn;
    fptr_p_void    get_device_binding_id;
48
    fptr_p_void    get_device_nonce;
anonymous's avatar
anonymous committed
49
    fptr_p_void    get_media_key;
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
};


static void _libaacs_close(BD_AACS *p)
{
    if (p->aacs) {
        DL_CALL(p->h_libaacs, aacs_close, p->aacs);
        p->aacs = NULL;
    }
}

void libaacs_unload(BD_AACS **p)
{
    if (p && *p) {
        _libaacs_close(*p);

        if ((*p)->h_libaacs) {
            dl_dlclose((*p)->h_libaacs);
        }

        X_FREE(*p);
    }
}

npzacs's avatar
npzacs committed
74
int libaacs_required(void *have_file_handle, int (*have_file)(void *, const char *, const char *))
75
{
npzacs's avatar
npzacs committed
76
    if (have_file(have_file_handle, "AACS", "Unit_Key_RO.inf")) {
77
        BD_DEBUG(DBG_BLURAY, "AACS" DIR_SEP "Unit_Key_RO.inf found. Disc seems to be AACS protected.\n");
78 79 80
        return 1;
    }

81
    BD_DEBUG(DBG_BLURAY, "AACS" DIR_SEP "Unit_Key_RO.inf not found. No AACS protection.\n");
82 83 84
    return 0;
}

85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
static void *_open_libaacs(void)
{
    const char * const libaacs[] = {
      getenv("LIBAACS_PATH"),
      "libaacs",
      "libmmbd",
    };
    unsigned ii;

    for (ii = 0; ii < sizeof(libaacs) / sizeof(libaacs[0]); ii++) {
        if (libaacs[ii]) {
            void *handle = dl_dlopen(libaacs[ii], "0");
            if (handle) {
                BD_DEBUG(DBG_BLURAY, "Using %s for AACS\n", libaacs[ii]);
                return handle;
            }
        }
    }

    BD_DEBUG(DBG_BLURAY | DBG_CRIT, "No usable AACS libraries found!\n");
    return NULL;
}

108 109 110
BD_AACS *libaacs_load(void)
{
    BD_AACS *p = calloc(1, sizeof(BD_AACS));
111 112 113
    if (!p) {
        return NULL;
    }
114

115
    p->h_libaacs = _open_libaacs();
116 117 118 119 120
    if (!p->h_libaacs) {
        X_FREE(p);
        return NULL;
    }

121
    BD_DEBUG(DBG_BLURAY, "Loading aacs library (%p)\n", p->h_libaacs);
122 123 124 125 126

    *(void **)(&p->decrypt_unit) = dl_dlsym(p->h_libaacs, "aacs_decrypt_unit");
    *(void **)(&p->get_vid)      = dl_dlsym(p->h_libaacs, "aacs_get_vid");
    *(void **)(&p->get_pmsn)     = dl_dlsym(p->h_libaacs, "aacs_get_pmsn");
    *(void **)(&p->get_device_binding_id) = dl_dlsym(p->h_libaacs, "aacs_get_device_binding_id");
127
    *(void **)(&p->get_device_nonce)      = dl_dlsym(p->h_libaacs, "aacs_get_device_nonce");
anonymous's avatar
anonymous committed
128
    *(void **)(&p->get_media_key)         = dl_dlsym(p->h_libaacs, "aacs_get_mk");
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145

    if (!p->decrypt_unit) {
        BD_DEBUG(DBG_BLURAY | DBG_CRIT, "libaacs dlsym failed! (%p)\n", p->h_libaacs);
        libaacs_unload(&p);
        return NULL;
    }

    BD_DEBUG(DBG_BLURAY, "Loaded libaacs (%p)\n", p->h_libaacs);

    if (file_open != file_open_default()) {
        BD_DEBUG(DBG_BLURAY, "Registering libaacs filesystem handler %p (%p)\n", (void *)(intptr_t)file_open, p->h_libaacs);
        DL_CALL(p->h_libaacs, aacs_register_file, file_open);
    }

    return p;
}

npzacs's avatar
npzacs committed
146 147 148 149
int libaacs_open(BD_AACS *p, const char *device,
                   void *file_open_handle, void *file_open_fp,
                   const char *keyfile_path)

150 151 152 153 154
{
    int error_code = 0;

    fptr_p_void open;
    fptr_p_void open2;
Petri Hintukainen's avatar
Petri Hintukainen committed
155 156
    fptr_p_void init;
    fptr_int    open_device;
157 158 159 160 161 162 163
    fptr_int    aacs_get_mkb_version;
    fptr_p_void aacs_get_disc_id;

    _libaacs_close(p);

    *(void **)(&open)  = dl_dlsym(p->h_libaacs, "aacs_open");
    *(void **)(&open2) = dl_dlsym(p->h_libaacs, "aacs_open2");
Petri Hintukainen's avatar
Petri Hintukainen committed
164
    *(void **)(&init)  = dl_dlsym(p->h_libaacs, "aacs_init");
165 166
    *(void **)(&aacs_get_mkb_version) = dl_dlsym(p->h_libaacs, "aacs_get_mkb_version");
    *(void **)(&aacs_get_disc_id)     = dl_dlsym(p->h_libaacs, "aacs_get_disc_id");
Petri Hintukainen's avatar
Petri Hintukainen committed
167 168 169 170
    *(void **)(&open_device)          = dl_dlsym(p->h_libaacs, "aacs_open_device");

    if (init && open_device) {
        p->aacs = init();
npzacs's avatar
npzacs committed
171 172
        DL_CALL(p->h_libaacs, aacs_set_fopen, p->aacs, file_open_handle, file_open_fp);
        error_code = open_device(p->aacs, device, keyfile_path);
Petri Hintukainen's avatar
Petri Hintukainen committed
173 174
    } else if (open2) {
        BD_DEBUG(DBG_BLURAY, "Using old aacs_open2(), no UDF support available\n");
npzacs's avatar
npzacs committed
175
        p->aacs = open2(device, keyfile_path, &error_code);
176 177
    } else if (open) {
        BD_DEBUG(DBG_BLURAY, "Using old aacs_open(), no verbose error reporting available\n");
npzacs's avatar
npzacs committed
178
        p->aacs = open(device, keyfile_path);
179 180 181 182 183 184 185 186 187
    } else {
        BD_DEBUG(DBG_BLURAY, "aacs_open() not found\n");
    }

    if (p->aacs) {
        if (aacs_get_mkb_version) {
            p->mkbv = aacs_get_mkb_version(p->aacs);
        }
        if (aacs_get_disc_id) {
Petri Hintukainen's avatar
Petri Hintukainen committed
188
            p->disc_id = (const uint8_t *)aacs_get_disc_id(p->aacs);
189 190 191 192 193 194 195
        }
        return error_code;
    }

    return error_code ? error_code : 1;
}

npzacs's avatar
npzacs committed
196 197 198 199
/*
 *
 */

200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
void libaacs_select_title(BD_AACS *p, uint32_t title)
{
    if (p && p->aacs) {
        DL_CALL(p->h_libaacs, aacs_select_title, p->aacs, title);
    }
}

int libaacs_decrypt_unit(BD_AACS *p, uint8_t *buf)
{
    if (p && p->aacs) {
        if (!p->decrypt_unit(p->aacs, buf)) {
            BD_DEBUG(DBG_AACS | DBG_CRIT, "Unable decrypt unit (AACS)!\n");

            return -1;
        } // decrypt
    } // aacs

    return 0;
}

npzacs's avatar
npzacs committed
220 221 222 223 224
/*
 *
 */

static const uint8_t *_get_vid(BD_AACS *p)
225 226 227 228 229 230 231 232 233
{
    if (!p->get_vid) {
        BD_DEBUG(DBG_BLURAY | DBG_CRIT, "aacs_get_vid() dlsym failed!\n");
        return NULL;
    }

    return (const uint8_t*)p->get_vid(p->aacs);
}

npzacs's avatar
npzacs committed
234
static const uint8_t *_get_pmsn(BD_AACS *p)
235 236 237 238 239 240 241 242 243
{
    if (!p->get_pmsn) {
        BD_DEBUG(DBG_BLURAY | DBG_CRIT, "aacs_get_pmsn() dlsym failed!\n");
        return NULL;
    }

    return (const uint8_t*)p->get_pmsn(p->aacs);
}

npzacs's avatar
npzacs committed
244
static const uint8_t *_get_device_binding_id(BD_AACS *p)
245 246 247 248 249 250 251 252 253
{
    if (!p->get_device_binding_id) {
        BD_DEBUG(DBG_BLURAY | DBG_CRIT, "aacs_get_device_binding_id() dlsym failed!\n");
        return NULL;
    }

    return (const uint8_t*)p->get_device_binding_id(p->aacs);
}

254 255 256 257 258 259 260 261 262 263
static const uint8_t *_get_device_nonce(BD_AACS *p)
{
    if (!p->get_device_nonce) {
        BD_DEBUG(DBG_BLURAY | DBG_CRIT, "aacs_get_device_nonce() dlsym failed!\n");
        return NULL;
    }

    return (const uint8_t*)p->get_device_nonce(p->aacs);
}

anonymous's avatar
anonymous committed
264 265 266 267 268 269 270 271 272 273
static const uint8_t *_get_media_key(BD_AACS *p)
{
    if (!p->get_media_key) {
        BD_DEBUG(DBG_BLURAY | DBG_CRIT, "aacs_get_mk() dlsym failed!\n");
        return NULL;
    }

    return (const uint8_t*)p->get_media_key(p->aacs);
}

npzacs's avatar
npzacs committed
274
uint32_t libaacs_get_mkbv(BD_AACS *p)
275
{
npzacs's avatar
npzacs committed
276
    return p ? p->mkbv : 0;
277 278
}

Petri Hintukainen's avatar
Petri Hintukainen committed
279 280 281 282 283 284 285 286 287 288 289 290 291
static const char *_type2str(int type)
{
    switch (type) {
    case BD_AACS_DISC_ID:            return "DISC_ID";
    case BD_AACS_MEDIA_VID:          return "MEDIA_VID";
    case BD_AACS_MEDIA_PMSN:         return "MEDIA_PMSN";
    case BD_AACS_DEVICE_BINDING_ID:  return "DEVICE_BINDING_ID";
    case BD_AACS_DEVICE_NONCE:       return "DEVICE_NONCE";
    case BD_AACS_MEDIA_KEY:          return "MEDIA_KEY";
    default: return "???";
    }
}

npzacs's avatar
npzacs committed
292
BD_PRIVATE const uint8_t *libaacs_get_aacs_data(BD_AACS *p, int type)
293
{
npzacs's avatar
npzacs committed
294
    if (!p || !p->aacs) {
Petri Hintukainen's avatar
Petri Hintukainen committed
295
        BD_DEBUG(DBG_BLURAY | DBG_CRIT, "get_aacs_data(%s): libaacs not initialized!\n", _type2str(type));
npzacs's avatar
npzacs committed
296 297 298 299 300
        return NULL;
    }

    switch (type) {
        case BD_AACS_DISC_ID:
301
            return p->disc_id;
npzacs's avatar
npzacs committed
302 303 304 305 306 307 308 309 310

        case BD_AACS_MEDIA_VID:
            return _get_vid(p);

        case BD_AACS_MEDIA_PMSN:
            return _get_pmsn(p);

        case BD_AACS_DEVICE_BINDING_ID:
            return _get_device_binding_id(p);
311 312 313

        case BD_AACS_DEVICE_NONCE:
            return _get_device_nonce(p);
anonymous's avatar
anonymous committed
314 315 316

        case BD_AACS_MEDIA_KEY:
            return _get_media_key(p);
npzacs's avatar
npzacs committed
317 318 319 320
    }

    BD_DEBUG(DBG_BLURAY | DBG_CRIT, "get_aacs_data(): unknown query %d\n", type);
    return NULL;
321
}