Petri Hintukainen pushed to branch master at VideoLAN / libaacs
Commits:
c9c8ad90 by npzacs at 2020-07-10T15:17:21+03:00
Add MKB and UK file dump tools
- - - - -
bcd6e023 by npzacs at 2020-07-10T15:18:04+03:00
log config summary
- - - - -
d1735467 by npzacs at 2020-07-10T15:18:46+03:00
Extend embedded keys table
- - - - -
ed63fc91 by npzacs at 2020-07-10T15:19:46+03:00
Cosmetics
- - - - -
48a25022 by npzacs at 2020-07-10T15:25:12+03:00
Convert unit keys in parser
- - - - -
7f63a556 by npzacs at 2020-07-10T15:27:01+03:00
Remove useless terminator entry from unit key list
- - - - -
1880ccc6 by npzacs at 2020-07-10T15:29:01+03:00
parser: free temporary string in parser loop
Avoid pointers to freed memory
- - - - -
0e591b58 by npzacs at 2020-07-10T15:49:27+03:00
parser: use static buffer for disc id and disc keys
Avoid >100k malloc() + free()
- - - - -
e9dc7188 by npzacs at 2020-07-10T15:49:27+03:00
load only current disc keys from config
- - - - -
57e3e276 by npzacs at 2020-07-10T15:49:27+03:00
parser: fix leak (ignore multiple UK entries)
- - - - -
e2229467 by hpi1 at 2020-07-10T15:49:27+03:00
Fix make dist (missing files)
- - - - -
76453650 by hpi1 at 2020-07-10T15:49:48+03:00
strutl: Remove duplicate function
- - - - -
58fb6beb by John Doe at 2020-07-10T16:07:37+03:00
Don't discard unit keys unless VUK is available
Previously libaacs discarded unit keys when e.g. config file had 1 unit
key and the disc had 2 or more. This is fine when we have VUK or MK+VID
available. However, when VUK is not available, discarding unit key means
that the disc can't be decrypted.
- - - - -
13 changed files:
- Makefile.am
- + src/devtools/mkb_dump.c
- src/devtools/parser_test.c
- + src/devtools/read_file.h
- + src/devtools/uk_dump.c
- src/file/keydb.h
- src/file/keydbcfg-parser.y
- src/file/keydbcfg.c
- src/file/keydbcfg.h
- src/libaacs/aacs.c
- src/libaacs/crypto.c
- src/util/strutl.c
- src/util/strutl.h
Changes:
=====================================
Makefile.am
=====================================
@@ -1,6 +1,6 @@
ACLOCAL_AMFLAGS=-I m4
-EXTRA_DIST=bootstrap COPYING KEYDB.cfg README.txt ChangeLog
+EXTRA_DIST=bootstrap COPYING KEYDB.cfg README.md ChangeLog
SET_FEATURES = @SET_FEATURES@
SET_INCLUDES = -I$(top_srcdir)/src -I$(top_builddir)/src/libaacs
@@ -93,7 +93,7 @@ dist-hook:
# programs
#
-noinst_PROGRAMS = parser_test
+noinst_PROGRAMS = parser_test uk_dump mkb_dump
bin_PROGRAMS = aacs_info
parser_test_SOURCES = \
@@ -104,6 +104,23 @@ parser_test_SOURCES = \
src/util/logging.c
parser_test_CFLAGS = -std=c99 $(SET_FEATURES) $(SET_INCLUDES)
+uk_dump_SOURCES = \
+ src/devtools/uk_dump.c \
+ src/devtools/read_file.h \
+ src/libaacs/unit_key.h \
+ src/libaacs/unit_key.c \
+ src/util/logging.c
+uk_dump_CFLAGS = -std=c99 $(SET_FEATURES) $(SET_INCLUDES)
+
+mkb_dump_SOURCES = \
+ src/devtools/mkb_dump.c \
+ src/devtools/read_file.h \
+ src/libaacs/mkb.h \
+ src/libaacs/mkb.c \
+ src/util/strutl.c \
+ src/util/logging.c
+mkb_dump_CFLAGS = -std=c99 $(SET_FEATURES) $(SET_INCLUDES)
+
aacs_info_SOURCES = src/examples/aacs_info.c
aacs_info_CFLAGS = -std=c99 $(SET_FEATURES) $(SET_INCLUDES)
aacs_info_LDADD = libaacs.la
=====================================
src/devtools/mkb_dump.c
=====================================
@@ -0,0 +1,260 @@
+/*
+ * This file is part of libaacs
+ * Copyright (C) 2020 VideoLAN
+ *
+ * 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/>.
+ */
+
+#include "util/strutl.h"
+#include "util/macro.h"
+#include "libaacs/mkb.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#include "read_file.h"
+
+static const struct {
+ uint8_t type;
+ const char *name;
+} rec_names[] = {
+ { 0x02, "End of media key block" },
+ { 0x04, "Explicit Subset-Difference" },
+ { 0x05, "Media Key Data / CVALUE" },
+ { 0x07, "Subset-Difference Index" },
+ { 0x0c, "Media Key Variant Data" },
+ { 0x10, "Type and version" },
+ { 0x20, "Drive revocation list (AACS1)" },
+ { 0x21, "Host revocation list (AACS1)" },
+ { 0x30, "Drive revocation list (AACS2)" },
+ { 0x31, "Host revocation list (AACS2)" },
+ { 0x81, "Verify media key / DVALUE" },
+ { 0x86, "Verify media key / DVALUE (AACS2)" },
+};
+
+static const struct {
+ uint32_t type;
+ const char *name;
+} mkb_types[] = {
+ { MKB_TYPE_3, "Type 3" },
+ { MKB_TYPE_4, "Type 4" },
+ { MKB_TYPE_10_CLASS_II, "Type 10, class II" },
+ { MKB_20_CATEGORY_C, "Type 2.0 Category C" },
+ { MKB_21_CATEGORY_C, "Type 2.1 Category C" },
+};
+
+static const char *rec_name(uint8_t type) {
+ size_t i;
+ for (i = 0; i < sizeof(rec_names)/sizeof(rec_names[0]); i++)
+ if (rec_names[i].type == type)
+ return rec_names[i].name;
+ return "(UNKNOWN)";
+}
+
+static const char *type_name(uint32_t type) {
+ size_t i;
+ for (i = 0; i < sizeof(mkb_types)/sizeof(mkb_types[0]); i++)
+ if (mkb_types[i].type == type)
+ return mkb_types[i].name;
+ return "(UNKNOWN)";
+}
+
+static void _dump_signature(const uint8_t *sig, size_t len)
+{
+ size_t i;
+
+ printf(" Signature (%zu bytes): ", len);
+ for (i = 0; i < len; i++) {
+ printf("%02x", sig[i]);
+ }
+ printf("\n");
+}
+
+static void _dump_aacs1_rl(const uint8_t *rl, size_t rl_size)
+{
+ if (rl_size < 4)
+ return;
+
+ uint32_t total_entries = MKINT_BE32(rl);
+ rl += 4; rl_size -= 4;
+
+ while (total_entries > 0 && rl_size >= 4) {
+ uint32_t entries = MKINT_BE32(rl);
+ rl += 4; rl_size -= 4;
+ if (entries >= (0xffffffff - 8 - 40) / 8) {
+ printf(" invalid revocation list\n");
+ break;
+ }
+
+ size_t rec_len = 8 * entries + 40;
+ if (rec_len > rl_size) {
+ printf(" revocation list size mismatch\n");
+ break;
+ }
+
+ unsigned ii;
+ for (ii = 0; ii < entries; ii++) {
+ uint16_t len = MKINT_BE16(rl);
+ uint64_t id = MKINT_BE48(rl + 2);
+ printf(" %3d: id %12" PRIx64, ii, id);
+ if (len) {
+ printf("-%12" PRIx64, id + len);
+ }
+ printf("\n");
+ rl += 8; rl_size -= 8;
+ }
+ _dump_signature(rl, 40);
+ rl += 40; rl_size -= 40;
+ total_entries -= entries;
+ }
+}
+
+static void _dump_type_and_version(const uint8_t *p, size_t size)
+{
+ if (size >= 4)
+ printf(" MKB type: 0x%08x (%s)\n", MKINT_BE32(p),
type_name(MKINT_BE32(p)));
+ if (size >= 8)
+ printf(" MKB version: %8d\n", MKINT_BE32(p+4));
+}
+
+static void _dump_record(MKB *mkb, int record)
+{
+ size_t size = mkb_data_size(mkb);
+ const uint8_t *data = mkb_data(mkb);
+ size_t pos, len;
+
+ printf("\nRecord 0x%02x (%s):\n", record, rec_name(record));
+
+ for (pos = 0; pos + 4 <= size; pos += len) {
+ uint8_t type = data[pos];
+ len = MKINT_BE24(data + pos + 1);
+ if (type == record) {
+ switch (record) {
+ case 0x02: _dump_signature(data + pos + 4, len - 4); break;
+ case 0x10: _dump_type_and_version(data + pos + 4, len - 4);
break;
+ case 0x20:
+ case 0x21: _dump_aacs1_rl(data + pos + 4, len - 4); break;
+ }
+ printf(" Raw data (%zu bytes):\n", len - 4);
+ const uint8_t *p = data + pos + 4;
+ const uint8_t *e = data + pos + len;
+ while (p < e) {
+ size_t i;
+ printf(" ");
+ for (i = 0; i < 8 && p < e; i++, p++)
+ printf("%02x ", *p);
+ printf(" ");
+ for (i = 0; i < 8 && p < e; i++, p++)
+ printf("%02x ", *p);
+ printf("\n");
+ }
+ break;
+ }
+ }
+}
+
+static void _list_missing_records(const uint8_t *seen_map)
+{
+ int type;
+
+ for (type = 0; type < 256; type++) {
+ if (!seen_map[type]) {
+ size_t i;
+ for (i = 0; i < sizeof(rec_names)/sizeof(rec_names[0]); i++) {
+ if (rec_names[i].type == type) {
+ printf(" %s\n", rec_names[i].name);
+ }
+ }
+ }
+ }
+}
+
+static void _list_records(MKB *mkb, uint8_t *seen_map)
+{
+ const uint8_t *data = mkb_data(mkb);
+ size_t size = mkb_data_size(mkb);
+ size_t pos, len;
+
+ for (pos = 0; pos + 4 <= size; pos += len) {
+ uint8_t type = data[pos];
+ len = MKINT_BE24(data + pos + 1);
+ printf(" record 0x%02x: %10zu bytes %s\n", type, len,
rec_name(type));
+ seen_map[type] = 1;
+ if (len == 0) {
+ printf(" UNKNOWN: %10zu bytes\n", size - pos);
+ break;
+ }
+ }
+}
+
+int main (int argc, char **argv)
+{
+ MKB *mkb;
+ uint8_t seen_map[256] = {0};
+ uint8_t *data;
+ size_t size;
+ int arg;
+
+ if (argc < 2) {
+ fprintf(stderr, "usage: mkb_dump <mkb_file> [record [record ...]]\n");
+ exit(-1);
+ }
+
+ size = _read_file(argv[1], 16, 1024*1024*1024, &data);
+ if (!size) {
+ exit(-1);
+ }
+
+ printf("MKB file size: %10zu bytes\n", size);
+
+ mkb = mkb_init(data, size);
+ if (!mkb) {
+ fprintf(stderr, "MKB init failed: %s (%d)\n", strerror(errno), errno);
+ free(data);
+ exit(-1);
+ }
+
+ printf("MKB size: %10zu bytes\n\n", mkb_data_size(mkb));
+
+ printf("MKB type: %s (0x%08x)\n", type_name(mkb_type(mkb)),
mkb_type(mkb));
+ printf("MKB version: %d\n", mkb_version(mkb));
+
+ printf("MKB records:\n");
+ _list_records(mkb, seen_map);
+ printf(" PADDING: %10zu bytes\n", size - mkb_data_size(mkb));
+
+ printf("\nNot present records:\n");
+ _list_missing_records(seen_map);
+
+ for (arg = 2; arg < argc; arg++) {
+ int record = strtol(argv[arg], NULL, 16);
+ _dump_record(mkb, record);
+ }
+
+ /* check padding */
+ for (size_t i = mkb_data_size(mkb); i < size; i++) {
+ if (data[i]) {
+ printf("\nData found from padding !\n");
+ break;
+ }
+ }
+
+ mkb_close(mkb);
+
+ return 0;
+}
=====================================
src/devtools/parser_test.c
=====================================
@@ -27,9 +27,13 @@
static int print_digit_key_pair_enties(digit_key_pair_list *list);
static int print_title_entries(title_entry_list *list);
+static const uint8_t empty_key[16] = {0};
+
/* Function to print the entres in a digit key pair list */
static int print_digit_key_pair_enties(digit_key_pair_list *list)
{
+ char tmp[256];
+
if (!list)
{
printf("Error: No digit key pair list passed as parameter.\n");
@@ -39,10 +43,7 @@ static int print_digit_key_pair_enties(digit_key_pair_list
*list)
digit_key_pair_list *cursor = list;
while (cursor)
{
- if (!cursor->key_pair.key)
- break;
-
- printf(" %u - %s\n", cursor->key_pair.digit, cursor->key_pair.key);
+ printf(" %u - %s\n", cursor->key_pair.digit, str_print_hex(tmp,
cursor->key_pair.key, 16));
cursor = cursor->next;
}
@@ -53,7 +54,6 @@ static int print_digit_key_pair_enties(digit_key_pair_list
*list)
/* Function that prints all entries parsed from a config file */
static int print_title_entries(title_entry_list *list)
{
- static const uint8_t empty_key[16] = {0};
char tmp[256];
if (!list)
@@ -166,15 +166,35 @@ static int print_config_file(config_file *cfgfile)
/* main */
int main (int argc, char **argv)
{
- if (argc != 2) {
- fprintf(stderr, "usage: parser_test [config_file]\n");
+ config_file *cfgfile;
+ uint8_t disc_id[20] = {0};
+ const uint8_t *want_disc_id = NULL;
+ int retval = 0;
+
+ if (argc < 2 || argc > 3) {
+ fprintf(stderr, "usage: parser_test [config_file [disc_id]]\n");
return EXIT_FAILURE;
}
- config_file *cfgfile = keydbcfg_new_config_file();
- int retval = keydbcfg_parse_config(cfgfile, argv[1]);
- retval &= print_config_file(cfgfile);
- keydbcfg_config_file_close(cfgfile);
+ if (argc > 2) {
+ if (strlen(argv[2]) != 40) {
+ fprintf(stderr, "disc id must be 40 chars long\n");
+ return EXIT_FAILURE;
+ }
+ if (!hexstring_to_hex_array(disc_id, 20, argv[2])) {
+ fprintf(stderr, "invalid disc id %s\n", argv[2]);
+ return EXIT_FAILURE;
+ }
+ want_disc_id = disc_id;
+ }
+
+
+ cfgfile = keydbcfg_new_config_file();
+ if (cfgfile) {
+ retval = keydbcfg_parse_config(cfgfile, argv[1], want_disc_id,
want_disc_id == NULL);
+ retval &= print_config_file(cfgfile);
+ keydbcfg_config_file_close(cfgfile);
+ }
if (!retval)
return EXIT_FAILURE;
=====================================
src/devtools/read_file.h
=====================================
@@ -0,0 +1,77 @@
+/*
+ * This file is part of libaacs
+ * Copyright (C) 2020 VideoLAN
+ *
+ * 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/>.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+static size_t _read_file(const char *name, off_t min_size, off_t max_size,
uint8_t **pdata)
+{
+ FILE *f;
+ uint8_t *data = NULL;
+ off_t file_size;
+ size_t size;
+
+ f = fopen(name, "rb");
+ if (!f) {
+ fprintf(stderr, "error opening '%s': %s (%d)\n", name,
strerror(errno), errno);
+ return 0;
+ }
+
+ if (fseek(f, 0, SEEK_END) < 0) {
+ fprintf(stderr, "error seeking to end: %s (%d)\n", strerror(errno),
errno);
+ goto fail;
+ }
+
+ file_size = ftell(f);
+
+ if (file_size < 0) {
+ fprintf(stderr, "error getting file size: %s (%d)\n", strerror(errno),
errno);
+ goto fail;
+ }
+ if (file_size < min_size || file_size > max_size) {
+ fprintf(stderr, "weird file size: %lld\n", (long long)file_size);
+ goto fail;
+ }
+ if (fseek(f, 0, SEEK_SET) < 0) {
+ fprintf(stderr, "error seeking to start: %s (%d)\n", strerror(errno),
errno);
+ goto fail;
+ }
+
+ size = (size_t)file_size;
+
+ data = malloc(size);
+ if (!data) {
+ fprintf(stderr, "error allocating %zu bytes: %s (%d)\n", size,
strerror(errno), errno);
+ goto fail;
+ }
+ if (fread(data, size, 1, f) != 1) {
+ fprintf(stderr, "error reading %zu bytes: %s (%d)\n", size,
strerror(errno), errno);
+ goto fail;
+ }
+ fclose(f);
+
+ *pdata = data;
+ return size;
+
+ fail:
+ free(data);
+ fclose(f);
+ return 0;
+}
=====================================
src/devtools/uk_dump.c
=====================================
@@ -0,0 +1,113 @@
+/*
+ * This file is part of libaacs
+ * Copyright (C) 2020 VideoLAN
+ *
+ * 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/>.
+ */
+
+#include "util/strutl.h"
+#include "util/macro.h"
+#include "libaacs/unit_key.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "read_file.h"
+
+static void _uk_dump(AACS_UK *uk)
+{
+ unsigned i, j;
+
+ printf("Num BDMV dir: %d\n", uk->num_bdmv_dir);
+ printf("Application type: %d\n", uk->app_type);
+ printf("Use_SKB_Unified_MKB_Flag: %d\n", uk->use_skb_mkb);
+
+ printf("Encrypted unit keys: %u\n", uk->num_uk);
+ for (i = 0; i < uk->num_uk; i++) {
+ printf(" %u: ", i + 1);
+ for (j = 0; j < 16; j++) {
+ printf("%02x", uk->enc_uk[i].key[j]);
+ }
+ printf("\n");
+ }
+
+ printf("Title CPS units (%d titles):\n", uk->num_titles);
+ if (uk->num_titles > 0)
+ printf(" FP: %u\n", uk->title_cps_unit[0]);
+ if (uk->num_titles > 1)
+ printf(" TM: %u\n", uk->title_cps_unit[1]);
+ for (i = 0; i < uk->num_titles; i++)
+ printf(" %2u: %u\n", i + 1, uk->title_cps_unit[i + 2]);
+}
+
+int main (int argc, char **argv)
+{
+ AACS_UK *uk;
+ uint8_t *data;
+ size_t size;
+ int aacs2 = argc > 2;
+ size_t l, b;
+
+ if (argc < 2) {
+ fprintf(stderr, "usage: uk_dump <Unit_Key_RO.inf> [AACS2]\n");
+ exit(-1);
+ }
+
+ size = _read_file(argv[1], 16, 1024*1024, &data);
+ if (!size) {
+ exit(-1);
+ }
+
+ printf("Unit key file size: %8zu bytes\n", size);
+ uk = uk_parse(data, size, aacs2);
+
+ if (!uk) {
+ fprintf(stderr, "Parsing failed\n");
+ exit(-1);
+ }
+
+ _uk_dump(uk);
+
+ printf("Raw data:\n");
+ uint32_t mask = 0;
+ for (l = 0; l < size; l += 16) {
+ mask <<= 8;
+ for (b = 0; b < 32 && l+b < size; b++)
+ mask |= data[l + b];
+ if (mask) {
+ printf("%04lx: ", (long)l);
+
+ for (b = 0; b < 8 && l+b < size; b++)
+ printf("%02x ", data[l + b]);
+ printf(" ");
+ for (b = 8; b < 16 && l+b < size; b++)
+ printf("%02x ", data[l + b]);
+ printf(" ");
+ for (b = 0; b < 8 && l+b < size; b++)
+ if (data[l+b]) printf("%02x ", data[l + b]); else printf(" ");
+ printf(" ");
+ for (b = 8; b < 16 && l+b < size; b++)
+ if (data[l+b]) printf("%02x ", data[l + b]); else printf(" ");
+
+ printf("\n");
+ }
+ }
+
+ uk_free(&uk);
+ free(data);
+
+ return 0;
+}
=====================================
src/file/keydb.h
=====================================
@@ -1,8 +1,6 @@
/* encrypted keys */
-static const uint32_t internal_device_number = 0;
-
static const uint8_t internal_dk_list[][21] = {
{
},
=====================================
src/file/keydbcfg-parser.y
=====================================
@@ -1,5 +1,22 @@
%code requires {
#include "file/keydbcfg.h"
+
+#define MAX_KEY_SIZE 128
+
+typedef struct {
+ title_entry_list *celist; /* current disc entry or NULL */
+ digit_key_pair_list *dkplist; /* current list */
+
+ const uint64_t *want_disc_id; /* parse only this disc (none if NULL) */
+ int all_discs; /* parse entries for all discs */
+
+ size_t hexkey_size;
+ union { /* make sure we're properly aligned */
+ char b[MAX_KEY_SIZE];
+ uint64_t u64[5];
+ } hexkey;
+} parser_state;
+
}
%code {
@@ -23,6 +40,7 @@
*/
#include "util/macro.h"
+#include "util/strutl.h"
#include <stdio.h>
#include <stdlib.h>
@@ -48,7 +66,6 @@
while (X) \
{ \
digit_key_pair_list *pnext = X->next;\
- X_FREE(X->key_pair.key); \
X_FREE(X); \
X = pnext; \
} \
@@ -78,19 +95,29 @@ static void add_pk_entry(config_file *cf, char *key);
static void add_cert_entry(config_file *cf, char *host_priv_key, char
*host_cert);
static title_entry_list *new_title_entry_list(void);
-static int add_entry(title_entry_list *list, int type, char *entry);
-static digit_key_pair_list *new_digit_key_pair_list(void);
-static digit_key_pair_list *add_digit_key_pair_entry(digit_key_pair_list *list,
- int type, unsigned int digit, char *key);
+static int add_entry(title_entry_list *list, int type, const char *entry);
+static digit_key_pair_list *new_digit_key_pair_entry(int type, unsigned int
digit, const char *key);
/*
static int add_date_entry(title_entry_list *list, unsigned int year,
unsigned int month, unsigned int day);
*/
-void yyerror (void *scanner, config_file *cf,
- title_entry_list *celist, digit_key_pair_list *dkplist,
- const char *msg);
+void yyerror (void *scanner, config_file *cf, parser_state *ps, const char
*msg);
extern int libaacs_yyget_lineno (void *scanner);
+static inline int _discid_cmp(const uint64_t *want_disc_id, const uint64_t
*disc_id)
+{
+ unsigned i;
+
+ /* We know input strings are valid hex strings, len 40:
+ * want_disc_id was created from binary data, disc_id was checked by lexer
and parser.
+ * -> we just need to make sure all letters are lower case (= bit 0x20 set)
+ */
+ for (i = 0; i < 40/sizeof(uint64_t); i++)
+ if (want_disc_id[i] != (disc_id[i] | UINT64_C(0x2020202020202020)))
+ return 0;
+ return 1;
+}
+
/* uncomment the line below for debugging */
// int yydebug = 1;
}
@@ -105,8 +132,7 @@ extern int libaacs_yyget_lineno (void *scanner);
%lex-param{void *scanner}
%parse-param{void *scanner}
%parse-param{config_file *cf}
-%parse-param{title_entry_list *celist}
-%parse-param{digit_key_pair_list *dkplist}
+%parse-param{parser_state *ps}
%union
{
@@ -147,7 +173,7 @@ extern int libaacs_yyget_lineno (void *scanner);
%token BAD_ENTRY
%type <string> discid disc_title
-%type <string> host_priv_key host_cert host_nonce host_key_point hexstring_list
+%type <string> host_priv_key host_cert host_nonce host_key_point
hexstring_list hexkey
%type <string> device_key device_node key_uv key_u_mask_shift
%%
config_file
@@ -283,20 +309,24 @@ newline_list
disc_info
: discid PUNCT_EQUALS_SIGN disc_title
{
- if (!cf->list) {
- celist = cf->list = new_title_entry_list();
+ if (ps->hexkey_size != 40) {
+ fprintf(stderr, "Ignoring invalid disc id: %s (len = %zu)\n", $1,
ps->hexkey_size);
+ ps->celist = NULL;
+ } else if (!ps->all_discs && (!ps->want_disc_id ||
!_discid_cmp(ps->want_disc_id, ps->hexkey.u64))) {
+ ps->celist = NULL; /* ignore this disc */
} else {
- for (; celist->next; celist = celist->next) ;
- celist->next = new_title_entry_list();
- celist = celist->next;
+ ps->celist = new_title_entry_list();
+ if (ps->celist) {
+ ps->celist->next = cf->list;
+ cf->list = ps->celist;
+ hexstring_to_hex_array(ps->celist->entry.discid, 20, ps->hexkey.b);
+ }
}
- add_entry(celist, ENTRY_TYPE_DISCID, $1);
- /*add_entry(celist, ENTRY_TYPE_TITLE, $3);*/
}
;
discid
- : hexstring_list
+ : hexkey
;
disc_title
@@ -329,23 +359,23 @@ date_entry
;
mek_entry
- : ENTRY_ID_MEK hexstring_list
+ : ENTRY_ID_MEK hexkey
{
- add_entry(celist, ENTRY_TYPE_MEK, $2);
+ add_entry(ps->celist, ENTRY_TYPE_MEK, $2);
}
;
vid_entry
- : ENTRY_ID_VID hexstring_list
+ : ENTRY_ID_VID hexkey
{
- add_entry(celist, ENTRY_TYPE_VID, $2);
+ add_entry(ps->celist, ENTRY_TYPE_VID, $2);
}
;
bn_entry
: ENTRY_ID_BN bn_data_list
{
- dkplist = NULL;
+ ps->dkplist = NULL;
}
;
@@ -369,16 +399,16 @@ bn_data
;
vuk_entry
- : ENTRY_ID_VUK hexstring_list
+ : ENTRY_ID_VUK hexkey
{
- add_entry(celist, ENTRY_TYPE_VUK, $2);
+ add_entry(ps->celist, ENTRY_TYPE_VUK, $2);
}
;
pak_entry
: ENTRY_ID_PAK pak_data_list
{
- dkplist = NULL;
+ ps->dkplist = NULL;
}
;
@@ -404,7 +434,7 @@ pak_data
tk_entry
: ENTRY_ID_TK tk_data_list
{
- dkplist = NULL;
+ ps->dkplist = NULL;
}
;
@@ -430,7 +460,7 @@ tk_data
uk_entry
: ENTRY_ID_UK uk_data_list
{
- dkplist = NULL;
+ ps->dkplist = NULL;
}
;
@@ -440,17 +470,54 @@ uk_data_list
;
uk_data
- : DIGIT PUNCT_HYPHEN hexstring_list
+ : DIGIT PUNCT_HYPHEN hexkey
{
- if (!dkplist)
+ if (ps->celist) {
+ if (!ps->dkplist)
{
- dkplist = new_digit_key_pair_list();
- celist->entry.uk = dkplist;
+ if (ps->celist->entry.uk) {
+ /* duplicate entry */
+ char disc_id[41];
+ fprintf(stderr, "Ignoring duplicate unit key entry for %s\n",
+ str_print_hex(disc_id, ps->celist->entry.discid, 20));
+ } else {
+ ps->dkplist = new_digit_key_pair_entry(ENTRY_TYPE_UK, $1, $3);
+ ps->celist->entry.uk = ps->dkplist;
+ }
+ } else {
+ ps->dkplist->next = new_digit_key_pair_entry(ENTRY_TYPE_UK, $1, $3);
+ if (ps->dkplist->next)
+ ps->dkplist = ps->dkplist->next;
+ }
}
- dkplist = add_digit_key_pair_entry(dkplist, ENTRY_TYPE_UK, $1, $3);
}
;
+hexkey
+ : hexkey HEXSTRING
+ {
+ size_t len = strlen($2);
+ if (ps->hexkey_size + len >= sizeof(ps->hexkey.b)) {
+ fprintf(stderr, "too long key: %s %s\n", ps->hexkey.b, $2);
+ } else {
+ memcpy(ps->hexkey.b + ps->hexkey_size, $2, len + 1);
+ ps->hexkey_size += len;
+ }
+ $$ = ps->hexkey.b;
+ }
+ | HEXSTRING
+ {
+ size_t len = strlen($1);
+ if (len >= sizeof(ps->hexkey.b)) {
+ fprintf(stderr, "too long key: %s\n", $1);
+ ps->hexkey.b[0] = 0;
+ ps->hexkey_size = 0;
+ } else {
+ memcpy(ps->hexkey.b, $1, len + 1);
+ ps->hexkey_size = len;
+ }
+ $$ = ps->hexkey.b;
+ }
hexstring_list
: hexstring_list HEXSTRING
{
@@ -473,8 +540,22 @@ hexstring_list
;
%%
/* Function to parse a config file */
-int keydbcfg_parse_config(config_file *cfgfile, const char *path)
+int keydbcfg_parse_config(config_file *cfgfile, const char *path, const
uint8_t *disc_id, int all_discs)
{
+ union { /* make sure we're properly aligned */
+ uint64_t u64[5];
+ char b[41];
+ } want_disc_id;
+
+ parser_state ps = {
+ .celist = NULL,
+ .dkplist = NULL,
+ .want_disc_id = NULL,
+ .all_discs = all_discs,
+ .hexkey_size = 0,
+ .hexkey.b = "",
+ };
+
if (!cfgfile || !path)
return 0;
@@ -490,10 +571,14 @@ int keydbcfg_parse_config(config_file *cfgfile, const
char *path)
if (!fp)
return 0;
+ if (disc_id) {
+ str_print_hex(want_disc_id.b, disc_id, 20);
+ ps.want_disc_id = want_disc_id.u64;
+ }
void *scanner;
libaacs_yylex_init(&scanner);
libaacs_yyset_in(fp, scanner);
- int retval = yyparse(scanner, cfgfile, cfgfile->list, NULL);
+ int retval = yyparse(scanner, cfgfile, &ps);
libaacs_yylex_destroy(scanner);
fclose(fp);
@@ -697,16 +782,14 @@ title_entry_list *new_title_entry_list(void)
#define CHECK_KEY_LENGTH(name, len) \
if (!entry || strlen(entry) != len) { \
fprintf(stderr, "Ignoring bad "name" entry %s\n", entry); \
- X_FREE(entry); \
break; \
}
/* Function to add standard string entries to a config entry */
-static int add_entry(title_entry_list *list, int type, char *entry)
+static int add_entry(title_entry_list *list, int type, const char *entry)
{
if (!list)
{
- fprintf(stderr, "Error: No title list passed as parameter.\n");
return 0;
}
@@ -715,7 +798,6 @@ static int add_entry(title_entry_list *list, int type, char
*entry)
case ENTRY_TYPE_DISCID:
CHECK_KEY_LENGTH("discid", 40)
hexstring_to_hex_array(list->entry.discid, 20, entry);
- X_FREE(entry);
break;
#if 0
@@ -728,23 +810,19 @@ static int add_entry(title_entry_list *list, int type,
char *entry)
case ENTRY_TYPE_MEK:
CHECK_KEY_LENGTH("mek", 32)
hexstring_to_hex_array(list->entry.mk, 16, entry);
- X_FREE(entry);
break;
case ENTRY_TYPE_VID:
CHECK_KEY_LENGTH("vid", 32)
hexstring_to_hex_array(list->entry.vid, 16, entry);
- X_FREE(entry);
break;
case ENTRY_TYPE_VUK:
CHECK_KEY_LENGTH("vuk", 32)
hexstring_to_hex_array(list->entry.vuk, 16, entry);
- X_FREE(entry);
break;
default:
- X_FREE(entry);
fprintf(stderr, "WARNING: entry type passed in unknown\n");
return 0;
}
@@ -752,32 +830,25 @@ static int add_entry(title_entry_list *list, int type,
char *entry)
return 1;
}
-/* Function that returns pointer to new digit key pair list */
-static digit_key_pair_list *new_digit_key_pair_list(void)
+/* Function used to add a digit/key pair to a list of digit key pair entries */
+static digit_key_pair_list *new_digit_key_pair_entry(int type, unsigned int
digit, const char *key)
{
- digit_key_pair_list *list = (digit_key_pair_list *)calloc(1, sizeof(*list));
- if (!list) {
- fprintf(stderr, "Error allocating memory for new digit key pair entry
list!\n");
+ digit_key_pair_list *list;
+
+ if (!key || strlen(key) != 32) {
+ fprintf(stderr, "Ignoring bad UK entry %s\n", key ? key : "<null>");
+ return NULL;
}
- return list;
-}
-/* Function used to add a digit/key pair to a list of digit key pair entries */
-static digit_key_pair_list *add_digit_key_pair_entry(digit_key_pair_list *list,
- int type, unsigned int digit, char *key)
-{
- if (!list)
- {
- fprintf(stderr, "Error: No digit key pair list passed as parameter.\n");
+ list = (digit_key_pair_list *)calloc(1, sizeof(*list));
+ if (!list) {
+ fprintf(stderr, "Error allocating memory for new digit key pair entry
list!\n");
return NULL;
}
list->key_pair.digit = digit;
- list->key_pair.key = key;
-
- list->next = new_digit_key_pair_list();
-
- return list->next;
+ hexstring_to_hex_array(list->key_pair.key, 16, key);
+ return list;
}
/* Function to add a date entry */
@@ -800,9 +871,7 @@ static int add_date_entry(title_entry_list *list, unsigned
int year,
#endif
/* Our definition of yyerror */
-void yyerror (void *scanner, config_file *cf,
- title_entry_list *celist, digit_key_pair_list *dkplist,
- const char *msg)
+void yyerror (void *scanner, config_file *cf, parser_state *ps, const char
*msg)
{
fprintf(stderr, "%s: line %d\n", msg, libaacs_yyget_lineno(scanner));
}
=====================================
src/file/keydbcfg.c
=====================================
@@ -313,7 +313,7 @@ static char *_keycache_file(const char *type, const uint8_t
*disc_id)
return NULL;
}
- hex_array_to_hexstring(disc_id_str, disc_id, 20);
+ str_print_hex(disc_id_str, disc_id, 20);
result = str_printf("%s"DIR_SEP"%s"DIR_SEP"%s"DIR_SEP"%s", cache_dir,
CFG_DIR, type, disc_id_str);
X_FREE(cache_dir);
@@ -331,7 +331,7 @@ int keycache_save(const char *type, const uint8_t *disc_id,
const uint8_t *key,
AACS_FILE_H *fp = file_open(file, "w");
if (fp) {
- hex_array_to_hexstring(key_str, key, len);
+ str_print_hex(key_str, key, len);
if (file_write(fp, key_str, len*2) == len*2) {
BD_DEBUG(DBG_FILE, "Wrote %s to %s\n", type, file);
@@ -547,7 +547,7 @@ int config_get(const char *name, uint32_t *len, void *buf)
}
-static int _load_config_file(config_file *cf, int system)
+static int _load_config_file(config_file *cf, int system, const uint8_t
*disc_id)
{
static const char cfg_file_name[] = CFG_FILE_NAME;
@@ -565,7 +565,7 @@ static int _load_config_file(config_file *cf, int system)
BD_DEBUG(DBG_FILE, "found config file: %s\n", cfg_file);
file_close(fp);
- result = keydbcfg_parse_config(cf, cfg_file);
+ result = keydbcfg_parse_config(cf, cfg_file, disc_id, 0);
}
X_FREE(cfg_file);
@@ -588,9 +588,9 @@ static int _parse_embedded(config_file *cf)
break;
decrypt_key(e->key, internal_dk_list[jj], 16);
- e->node = internal_device_number;
- e->uv = MKINT_BE32(internal_dk_list[jj] + 16);
- e->u_mask_shift = internal_dk_list[jj][20];
+ e->node = MKINT_BE16(internal_dk_list[jj] + 16);
+ e->uv = MKINT_BE32(internal_dk_list[jj] + 16 + 2);
+ e->u_mask_shift = internal_dk_list[jj][20 + 2];
if (!e->uv || _is_duplicate_dk(cf->dkl, e)) {
X_FREE(e);
@@ -646,7 +646,26 @@ static int _parse_embedded(config_file *cf)
return result;
}
-config_file *keydbcfg_config_load(const char *configfile_path)
+static void _config_summary(config_file *cf)
+{
+ int n;
+ dk_list *dkl = cf->dkl;
+ pk_list *pkl = cf->pkl;
+ cert_list *hcl = cf->host_cert_list;
+ title_entry_list *tel = cf->list;
+
+ BD_DEBUG(DBG_AACS, "Config summary:\n");
+ for (n = 0; dkl; dkl = dkl->next, n++) ;
+ BD_DEBUG(DBG_AACS, " %d Device keys\n", n);
+ for (n = 0; pkl; pkl = pkl->next, n++) ;
+ BD_DEBUG(DBG_AACS, " %d Processing keys\n", n);
+ for (n = 0; hcl; hcl = hcl->next, n++) ;
+ BD_DEBUG(DBG_AACS, " %d Host certificates\n", n);
+ for (n = 0; tel; tel = tel->next, n++) ;
+ BD_DEBUG(DBG_AACS, " %d Disc entries\n", n);
+}
+
+config_file *keydbcfg_config_load(const char *configfile_path, const uint8_t
*disc_id)
{
int config_ok = 0;
@@ -658,14 +677,14 @@ config_file *keydbcfg_config_load(const char
*configfile_path)
/* try to load KEYDB.cfg */
if (configfile_path) {
- config_ok = keydbcfg_parse_config(cf, configfile_path);
+ config_ok = keydbcfg_parse_config(cf, configfile_path, disc_id, 0);
} else {
/* If no configfile path given, check for config files in user's home
and
* under /etc.
*/
- config_ok = _load_config_file(cf, 0);
- config_ok = _load_config_file(cf, 1) || config_ok;
+ config_ok = _load_config_file(cf, 0, disc_id);
+ config_ok = _load_config_file(cf, 1, disc_id) || config_ok;
}
/* Try to load simple (aacskeys) config files */
@@ -682,6 +701,8 @@ config_file *keydbcfg_config_load(const char
*configfile_path)
return NULL;
}
+ _config_summary(cf);
+
return cf;
}
=====================================
src/file/keydbcfg.h
=====================================
@@ -30,7 +30,7 @@ typedef struct digit_key_pair_t digit_key_pair;
struct digit_key_pair_t
{
unsigned int digit;
- char *key;
+ uint8_t key[16];
};
/* list of digit_key_pair struct used in title entry */
@@ -116,14 +116,13 @@ struct config_file_t
title_entry_list *list;
};
-/* Functions used throughout the parser */
-BD_PRIVATE int keydbcfg_parse_config(config_file *cfgfile, const char *path);
+BD_PRIVATE int keydbcfg_parse_config(config_file *cfgfile, const char *path,
const uint8_t *disc_id, int all_discs);
BD_PRIVATE config_file *keydbcfg_new_config_file(void);
BD_PRIVATE int keydbcfg_config_file_close(config_file *cfgfile);
/* */
-BD_PRIVATE config_file *keydbcfg_config_load(const char *configfile_path);
+BD_PRIVATE config_file *keydbcfg_config_load(const char *configfile_path,
const uint8_t *disc_id);
BD_PRIVATE int keycache_save(const char *type, const uint8_t *disc_id,
const uint8_t *key, unsigned int len);
=====================================
src/libaacs/aacs.c
=====================================
@@ -829,9 +829,10 @@ static int _calc_vuk(AACS *aacs, uint8_t *mk, uint8_t
*vuk, config_file *cf)
}
/* Function that collects keys from keydb config entry */
-static void _find_config_entry(AACS *aacs, title_entry_list *ce,
+static void _find_config_entry(AACS *aacs, config_file *cf,
uint8_t *mk, uint8_t *vuk)
{
+ title_entry_list *ce = cf->list;
char str[48];
char str2[48];
@@ -876,20 +877,29 @@ static void _find_config_entry(AACS *aacs,
title_entry_list *ce,
/* count keys */
unsigned num_uks = 0;
digit_key_pair_list *ukcursor = ce->entry.uk;
- while (ukcursor && ukcursor->key_pair.key) {
+ while (ukcursor) {
num_uks++;
ukcursor = ukcursor->next;
}
- /* check against Unit_Key_RO.inf */
+ /* check against Unit_Key_RO.inf, only discard incorrect amount of UKs
if VUK or MK+VID is available */
if (num_uks != aacs->uk->num_uk) {
- BD_DEBUG(DBG_CRIT | DBG_AACS, "Ignoring unit keys from config file
(expected %u keys, found %u)\n",
+ if (_calc_vuk(aacs, mk, vuk, cf) == AACS_SUCCESS) {
+ BD_DEBUG(DBG_CRIT | DBG_AACS, "Ignoring unit keys from config
file (expected %u keys, found %u)\n",
+ aacs->uk->num_uk, num_uks);
+ return;
+ }
+ BD_DEBUG(DBG_CRIT | DBG_AACS, "Incomplete unit keys in config file
(expected %u keys, found %u)\n",
aacs->uk->num_uk, num_uks);
- return;
}
/* get keys */
+ if (num_uks > aacs->uk->num_uk) {
+ /* config file has more unit keys than the disc has */
+ aacs->uk->num_uk = num_uks;
+ }
+
aacs->uk->uk = calloc(aacs->uk->num_uk, sizeof(aacs->uk->uk[0]));
if (!aacs->uk->uk) {
return;
@@ -897,8 +907,8 @@ static void _find_config_entry(AACS *aacs, title_entry_list
*ce,
num_uks = 0;
ukcursor = ce->entry.uk;
- while (ukcursor && ukcursor->key_pair.key) {
- hexstring_to_hex_array(aacs->uk->uk[num_uks].key, 16,
ukcursor->key_pair.key);
+ while (ukcursor) {
+ memcpy(aacs->uk->uk[num_uks].key, ukcursor->key_pair.key, 16);
BD_DEBUG(DBG_AACS, "Unit key %u from keydb entry: %s\n",
num_uks,
@@ -992,19 +1002,18 @@ static int _calc_uks(AACS *aacs, config_file *cf)
vuk_error_code = _calc_vuk(aacs, mk, vuk, NULL);
if (vuk_error_code != AACS_SUCCESS) {
- if (cf) {
- BD_DEBUG(DBG_AACS, "Searching for keydb config entry...\n");
- _find_config_entry(aacs, cf->list, mk, vuk);
+ if (cf) {
+ BD_DEBUG(DBG_AACS, "Searching for keydb config entry...\n");
+ _find_config_entry(aacs, cf, mk, vuk);
- /* Skip if retrieved from config file */
- if (aacs->uk->uk) {
- return AACS_SUCCESS;
+ /* Skip if retrieved from config file */
+ if (aacs->uk->uk) {
+ return AACS_SUCCESS;
+ }
}
- }
-
- /* Try to calculate VUK */
- vuk_error_code = _calc_vuk(aacs, mk, vuk, cf);
+ /* Try to calculate VUK */
+ vuk_error_code = _calc_vuk(aacs, mk, vuk, cf);
}
BD_DEBUG(DBG_AACS, "Calculate CPS unit keys...\n");
@@ -1307,7 +1316,7 @@ int aacs_open_device(AACS *aacs, const char *path, const
char *configfile_path)
return error_code;
}
- cf = keydbcfg_config_load(configfile_path);
+ cf = keydbcfg_config_load(configfile_path, aacs->disc_id);
BD_DEBUG(DBG_AACS, "Starting AACS waterfall...\n");
error_code = _calc_uks(aacs, cf);
@@ -1461,7 +1470,7 @@ const uint8_t *aacs_get_bdj_root_cert_hash(AACS *aacs)
const uint8_t *aacs_get_mk(AACS *aacs)
{
if (!memcmp(aacs->mk, empty_key, sizeof(aacs->mk))) {
- config_file *cf = keydbcfg_config_load(NULL);
+ config_file *cf = keydbcfg_config_load(NULL, NULL);
if (cf) {
uint8_t mk[16] = {0};
if (_calc_mk(aacs, mk, cf->pkl, cf->dkl) == AACS_SUCCESS) {
@@ -1489,7 +1498,7 @@ const uint8_t *aacs_get_vid(AACS *aacs)
return aacs->vid;
}
- config_file *cf = keydbcfg_config_load(NULL);
+ config_file *cf = keydbcfg_config_load(NULL, NULL);
if (cf) {
_read_vid(aacs, cf->host_cert_list);
@@ -1508,7 +1517,7 @@ const uint8_t *aacs_get_vid(AACS *aacs)
const uint8_t *aacs_get_pmsn(AACS *aacs)
{
if (!memcmp(aacs->pmsn, empty_key, sizeof(aacs->pmsn))) {
- config_file *cf = keydbcfg_config_load(NULL);
+ config_file *cf = keydbcfg_config_load(NULL, NULL);
if (cf) {
_read_pmsn(aacs, cf->host_cert_list);
=====================================
src/libaacs/crypto.c
=====================================
@@ -325,7 +325,7 @@ static gcry_error_t _aacs_sexp_key(gcry_sexp_t *p_sexp_key,
/* Points are currently only supported in standard format, so get a
* hexstring out of Q.
*/
- hex_array_to_hexstring(str_Q, Q, 1 + 2*key_len);
+ str_print_hex(str_Q, Q, 1 + 2*key_len);
char *strfmt = str_printf(
"(%s"
=====================================
src/util/strutl.c
=====================================
@@ -141,20 +141,6 @@ int hexstring_to_hex_array(uint8_t *hex_array, uint32_t
size,
return 1;
}
-/* Function to convert a hex array into a hex string.
- * str must be allocated by caller
- * size is the size of the hex_array
- */
-void hex_array_to_hexstring(char *str, const uint8_t *hex_array, uint32_t size)
-{
- unsigned int i;
-
- for (i = 0; i < size; i++)
- {
- sprintf(str + (i*2), "%02x", hex_array[i]);
- }
-}
-
char *str_dup(const char *str)
{
char *dup = NULL;
=====================================
src/util/strutl.h
=====================================
@@ -26,7 +26,6 @@
BD_PRIVATE int hexstring_to_hex_array(uint8_t *hex_array, uint32_t size,
const char *hexstring);
-BD_PRIVATE void hex_array_to_hexstring(char *str, const uint8_t *hex_array,
uint32_t size);
BD_PRIVATE char * str_dup(const char *str) BD_ATTR_MALLOC;
BD_PRIVATE char * str_printf(const char *fmt, ...) BD_ATTR_FORMAT_PRINTF(1,2)
BD_ATTR_MALLOC;
View it on GitLab:
https://code.videolan.org/videolan/libaacs/-/compare/7eab17148dfc5932cbd99457295355c40a6d1668...58fb6beb63f0097f04f9a3a48d84b691bece2df4
--
View it on GitLab:
https://code.videolan.org/videolan/libaacs/-/compare/7eab17148dfc5932cbd99457295355c40a6d1668...58fb6beb63f0097f04f9a3a48d84b691bece2df4
You're receiving this email because of your account on code.videolan.org.
_______________________________________________
libaacs-devel mailing list
[email protected]
https://mailman.videolan.org/listinfo/libaacs-devel