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
libaacs-devel@videolan.org
https://mailman.videolan.org/listinfo/libaacs-devel

Reply via email to