libaacs | branch: master | npzacs <npz...@gmail.com> | Sun Apr 26 18:51:26 2015 +0300| [b56e1db8a146f81ddbf96675d9624bd846628353] | committer: npzacs
Verify content certificate signature > http://git.videolan.org/gitweb.cgi/libaacs.git/?a=commit;h=b56e1db8a146f81ddbf96675d9624bd846628353 --- ChangeLog | 1 + Makefile.am | 2 ++ src/libaacs/aacs.c | 74 +++++++++++++++++++++++++++++++++++------ src/libaacs/content_cert.c | 78 ++++++++++++++++++++++++++++++++++++++++++++ src/libaacs/content_cert.h | 38 +++++++++++++++++++++ src/libaacs/crypto.c | 10 ++++++ src/libaacs/crypto.h | 1 + 7 files changed, 194 insertions(+), 10 deletions(-) diff --git a/ChangeLog b/ChangeLog index 42a61d4..d5d7e18 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,4 @@ +- Verify content certificate signature. - Fix build with gcrypt < 1.6.0. 2015-03-13: Version 0.8.1 diff --git a/Makefile.am b/Makefile.am index 22dbaca..c624ab1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -12,6 +12,8 @@ lib_LTLIBRARIES = libaacs.la libaacs_la_SOURCES=\ src/libaacs/aacs.h \ src/libaacs/aacs.c \ + src/libaacs/content_cert.h \ + src/libaacs/content_cert.c \ src/libaacs/crypto.h \ src/libaacs/crypto.c \ src/libaacs/mkb.h \ diff --git a/src/libaacs/aacs.c b/src/libaacs/aacs.c index 6c13f17..60a1a1f 100644 --- a/src/libaacs/aacs.c +++ b/src/libaacs/aacs.c @@ -26,6 +26,7 @@ #include "aacs-version.h" #include "aacs.h" +#include "content_cert.h" #include "crypto.h" #include "mmc.h" #include "mkb.h" @@ -81,6 +82,9 @@ struct aacs { int bec; /* bus encryption capable flag in drive certificate */ uint8_t read_data_key[16]; + /* content certificate */ + CONTENT_CERT *cc; + /* AACS Online (BD-J) */ uint8_t device_nonce[16]; uint8_t device_binding_id[16]; @@ -843,27 +847,73 @@ static int _calc_title_hash(AACS *aacs) return result; } -static int _get_bus_encryption_enabled(AACS *aacs) +static size_t _read_file(AACS *aacs, const char *file, void **data) { AACS_FILE_H *fp = NULL; - uint8_t buf[2]; - int bee = 0; + int64_t f_size; + + *data = NULL; - fp = _file_open(aacs, "AACS" DIR_SEP "Content000.cer"); + fp = _file_open(aacs, file); if (!fp) { - BD_DEBUG(DBG_AACS | DBG_CRIT, "Unable to open content certificate (AACS/Content000.cer)\n"); + BD_DEBUG(DBG_AACS | DBG_CRIT, "Unable to open %s\n", file); return 0; } - if (file_read(fp, buf, 2) == 2) { - bee = (buf[1] & 0x80) >> 7; - BD_DEBUG(DBG_AACS, "Bus Encryption Enabled flag in content certificate: %d\n", bee); + file_seek(fp, 0, SEEK_END); + f_size = file_tell(fp); + file_seek(fp, 0, SEEK_SET); + + *data = malloc(f_size); + if (*data) { + if (file_read(fp, *data, f_size) != f_size) { + BD_DEBUG(DBG_AACS | DBG_CRIT, "Failed reading %s\n", file); + X_FREE(*data); + } } else { - BD_DEBUG(DBG_AACS | DBG_CRIT, "Failed to read Bus Encryption Enabled flag from content certificate file\n"); + BD_DEBUG(DBG_AACS | DBG_CRIT, "Out of memory\n"); } file_close(fp); - return bee; + + return *data ? f_size : 0; +} + +static CONTENT_CERT *_read_cc_any(AACS *aacs) +{ + CONTENT_CERT *cc = NULL; + void *data; + size_t size; + + size = _read_file(aacs, "AACS" DIR_SEP "Content000.cer", &data); + if (!size) { + size = _read_file(aacs, "AACS" DIR_SEP "Content001.cer", &data); + } + + if (size) { + cc = cc_parse(data, size); + X_FREE(data); + } else { + BD_DEBUG(DBG_AACS | DBG_CRIT, "Failed to read content certificate file\n"); + } + + return cc; +} + +static int _get_bus_encryption_enabled(AACS *aacs) +{ + if (!aacs->cc) { + BD_DEBUG(DBG_AACS | DBG_CRIT, "Failed to read Bus Encryption Enabled flag from content certificate file\n"); + return 0; + } + + if (aacs->cc->bus_encryption_enabled_flag) { + BD_DEBUG(DBG_AACS, "Bus Encryption Enabled flag in content certificate: %d\n", + aacs->cc->bus_encryption_enabled_flag); + return 1; + } + + return 0; } static int _get_bus_encryption_capable(const char *path) @@ -1023,6 +1073,8 @@ int aacs_open_device(AACS *aacs, const char *path, const char *configfile_path) BD_DEBUG(DBG_AACS, "Failed to initialize AACS!\n"); } + aacs->cc = _read_cc_any(aacs); + aacs->bee = _get_bus_encryption_enabled(aacs); aacs->bec = _get_bus_encryption_capable(path); @@ -1059,6 +1111,8 @@ void aacs_close(AACS *aacs) X_FREE(aacs->cps_units); X_FREE(aacs->path); + cc_free(&aacs->cc); + BD_DEBUG(DBG_AACS, "AACS destroyed!\n"); /* erase sensitive data */ diff --git a/src/libaacs/content_cert.c b/src/libaacs/content_cert.c new file mode 100644 index 0000000..995537a --- /dev/null +++ b/src/libaacs/content_cert.c @@ -0,0 +1,78 @@ +/* + * This file is part of libaacs + * Copyright (C) 2015 npzacs + * + * 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 "content_cert.h" + +#include "crypto.h" + +#include "util/logging.h" +#include "util/macro.h" + +#include <stdlib.h> +#include <stdint.h> +#include <string.h> + +CONTENT_CERT *cc_parse(const void *data, size_t len) +{ + const uint8_t *p = data; + + if (len < 87) { + BD_DEBUG(DBG_AACS | DBG_CRIT, "Invalid content certificate (length %zd < 87)\n", len); + return NULL; + } + if (p[0] != 0) { + BD_DEBUG(DBG_AACS | DBG_CRIT, "Invalid content certificate type 0x%02x\n", p[0]); + return NULL; + } + + /* calculate certificate length */ + + size_t length_format_specific = MKINT_BE16(p + 24); + size_t num_digest = MKINT_BE16(p + 12); + + size_t cert_data_len = 26 + length_format_specific + num_digest*8; + + if (len < cert_data_len + 40) { + BD_DEBUG(DBG_AACS | DBG_CRIT, "Invalid content certificate (length %zd < %zd)\n", + len, cert_data_len + 40); + return NULL; + } + + /* check signature */ + + if (!crypto_aacs_verify_aacscc(p + cert_data_len, p, cert_data_len)) { + BD_DEBUG(DBG_AACS | DBG_CRIT, "Invalid content certificate signature\n"); + return NULL; + } + + /* return useful data */ + + CONTENT_CERT *c = calloc(1, sizeof(CONTENT_CERT)); + + c->bus_encryption_enabled_flag = p[1] >> 7; + + return c; +} + +void cc_free(CONTENT_CERT **pc) +{ + if (pc && *pc) { + X_FREE(*pc); + } +} diff --git a/src/libaacs/content_cert.h b/src/libaacs/content_cert.h new file mode 100644 index 0000000..b491aba --- /dev/null +++ b/src/libaacs/content_cert.h @@ -0,0 +1,38 @@ +/* + * This file is part of libaacs + * Copyright (C) 2015 npzacs + * + * 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/>. + */ + +#ifndef CONTENT_CERT_H_ +#define CONTENT_CERT_H_ + +#include "util/attributes.h" + +#include <stdlib.h> +#include <stdint.h> + +typedef struct content_cert CONTENT_CERT; + +struct content_cert { + uint8_t bus_encryption_enabled_flag; +}; + + +BD_PRIVATE CONTENT_CERT *cc_parse(const void *data, size_t len); +BD_PRIVATE void cc_free(CONTENT_CERT **); + +#endif /* CONTENT_CERT_H_ */ diff --git a/src/libaacs/crypto.c b/src/libaacs/crypto.c index 1294366..64a403e 100644 --- a/src/libaacs/crypto.c +++ b/src/libaacs/crypto.c @@ -539,6 +539,16 @@ int crypto_aacs_verify_aacsla(const uint8_t *signature, const uint8_t *data, ui return !_aacs_verify(signature, aacs_la_pubkey_x, aacs_la_pubkey_y, data, len); } +int crypto_aacs_verify_aacscc(const uint8_t *signature, const uint8_t *data, uint32_t len) +{ + static const uint8_t aacs_cc_pubkey_x[] = { 0x78, 0x4C, 0xF5, 0xC3, 0x63, 0x97, 0xA4, 0x39, 0x04, 0x06, + 0xA4, 0x9F, 0x78, 0x00, 0xC7, 0x7D, 0xE9, 0x0C, 0xB3, 0x4C }; + static const uint8_t aacs_cc_pubkey_y[] = { 0x00, 0x1D, 0xF3, 0x6B, 0x8F, 0x2E, 0xCF, 0x83, 0xCD, 0xEE, + 0x43, 0x8F, 0x7F, 0xD1, 0xF4, 0x80, 0x6F, 0xD2, 0x0D, 0xE7 }; + + return !_aacs_verify(signature, aacs_cc_pubkey_x, aacs_cc_pubkey_y, data, len); +} + int crypto_aacs_verify_cert(const uint8_t *cert) { if (MKINT_BE16(cert+2) != 0x5c) { diff --git a/src/libaacs/crypto.h b/src/libaacs/crypto.h index 0419927..00410f1 100644 --- a/src/libaacs/crypto.h +++ b/src/libaacs/crypto.h @@ -38,6 +38,7 @@ BD_PRIVATE void crypto_aacs_title_hash(const uint8_t *ukf, uint64_t len, uint8_t BD_PRIVATE int crypto_aacs_verify(const uint8_t *cert, const uint8_t *signature, const uint8_t *data, uint32_t len); BD_PRIVATE int crypto_aacs_verify_aacsla(const uint8_t *signature, const uint8_t *data, uint32_t len); +BD_PRIVATE int crypto_aacs_verify_aacscc(const uint8_t *signature, const uint8_t *data, uint32_t len); BD_PRIVATE int crypto_aacs_verify_cert(const uint8_t *cert); BD_PRIVATE int crypto_aacs_verify_host_cert(const uint8_t *cert); BD_PRIVATE int crypto_aacs_verify_drive_cert(const uint8_t *cert); _______________________________________________ libaacs-devel mailing list libaacs-devel@videolan.org https://mailman.videolan.org/listinfo/libaacs-devel