libaacs | branch: master | Janusz Dziemidowicz <rrapt...@nails.eu.org> | Sat 
Sep  7 01:54:09 2013 +0200| [583df16fa867ccda948a6c760ee8912684fb7b9d] | 
committer: hpi1

Support for AACS bus encryption

Due to the fact that AACS bus encryption was only hinted by early AACS
specification there seems to be some misconceptions.

First, what bus encryption is _not_:
- it does not encrypt all communication between drive and host
- it does not encrypt VID retrieval
- it does not use bus key (this is only poor wording in the
  specification)

Second, what is required for bus encryption to be activated:
- a bus encryption capable drive, drive certificate will have 0x01 as
  a second byte
- a bus encryption capable disc, content certificate (located in
  AACS/Content000.cer) will have 0x80 as a second byte
- a bus encryption capable host, host certificate will have 0x01 as a
  second byte

There are various combinations of all of those flags, so let's provide
a short summary:
- if drive is not bus encryption capable, then bus encryption will not
  be used, other flags are not relevant and normal AACS procedure will
  work as usual
- if drive is bus encryption capable but disc is not bus encryption
  enabled then bus encryption will not be used; however, drive will
  only allow hosts with bus encryption capable certificates, without
  one normal VID retrieval will fail, but getting VID from other
  source will make the disc playable
- if drive is bus encryption capable and disc is bus encryption
  enabled then bus encryption will be used, only hosts with bus
  encryption capable certificates can read such discs; getting VID
  from other source is not enought to read such disc as one must also
  have Read Data Key to decrypt bus encryption which is drive specific
While most of the current drives are supposed to be bus encryption
capable, most of the discs currently are not and it is quite hard to
come across one. Obviously this might change in the future.

So what is encrypted by bus encryption? Excatly the same data that is
encrypted by normal AACS, this means .m2ts files located in
BDMV/STREAM directory. Only this and nothing else. Bus encryption is
applied on the fly by the drive. Since the disc is already AACS
encrypted the host must first decrypt bus encryption and then perform
normal AACS decryption. So what is the difference? Bus encryption uses
encryption key that is drive specific, this means that the same disc
read on another drive model will produce differently encrypted
data. Without bus encryption, files simply copied from disc can be
decrypted if one gets proper VID. With bus encryption, such copy is
useless, unless proper decryption key is retrieved from the exact same
model of the drive. I am not sure if the encryption key is specific to
the drive model or every drive unit will have a different one.

This and several previous commits implement everything that is needed
to support bus encryption:
- determining if bus encryption is enabled from certificates
- retrieval of read data key that is used to encrypt data
- proper decryption (bus encryption works on sector boundary) before
  main AACS decryption

Code was tested with mplayer on LG BH16NS40 with "The Alien Anthology
Archives" disc from Alien Anthology (it is the only bus encryption
enabled disc out of 6 in the anthology).

> http://git.videolan.org/gitweb.cgi/libaacs.git/?a=commit;h=583df16fa867ccda948a6c760ee8912684fb7b9d
---

 ChangeLog          |    2 +
 src/libaacs/aacs.c |  115 +++++++++++++++++++++++++++++++++++++++++++++-------
 src/libaacs/mmc.c  |    4 --
 3 files changed, 102 insertions(+), 19 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index bc825c1..2ba6e20 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,5 @@
+ - Bus encryption support
+
 2013-03-04: Version 0.6.0
  - Added reading of PMSN (Pre-recorded Media Serial Number)
  - Fix resource leak
diff --git a/src/libaacs/aacs.c b/src/libaacs/aacs.c
index 741c5e8..a4ff22e 100644
--- a/src/libaacs/aacs.c
+++ b/src/libaacs/aacs.c
@@ -69,6 +69,7 @@ struct aacs {
     /* bus encryption */
     int       bee;        /* bus encryption enabled flag in content 
certificate */
     int       bec;        /* bus encryption capable flag in drive certificate 
*/
+    uint8_t   read_data_key[16];
 };
 
 static const uint8_t empty_key[] = "\x00\x00\x00\x00\x00\x00\x00\x00"
@@ -330,6 +331,53 @@ static int _read_vid(AACS *aacs, cert_list *hcl, int 
force_mmc)
     return error_code;
 }
 
+static int _read_read_data_key(AACS *aacs, cert_list *hcl)
+{
+    MMC* mmc = NULL;
+    if (!(mmc = mmc_open(aacs->path))) {
+        return AACS_ERROR_MMC_OPEN;
+    }
+
+    int error_code = AACS_ERROR_NO_CERT;
+
+    for (;hcl && hcl->host_priv_key && hcl->host_cert; hcl = hcl->next) {
+
+        char tmp_str[2*92+1];
+        uint8_t priv_key[20], cert[92];
+        hexstring_to_hex_array(priv_key, sizeof(priv_key), hcl->host_priv_key);
+        hexstring_to_hex_array(cert,     sizeof(cert),     hcl->host_cert);
+
+        if (!crypto_aacs_verify_host_cert(cert)) {
+            DEBUG(DBG_AACS, "Not using invalid host certificate %s.\n",
+                  print_hex(tmp_str, cert, 92));
+            continue;
+        }
+
+        DEBUG(DBG_AACS, "Trying host certificate (id 0x%s)...\n",
+              print_hex(tmp_str, cert + 4, 6));
+
+        int mmc_result = mmc_read_data_keys(mmc, priv_key, cert, 
aacs->read_data_key, NULL);
+
+        switch (mmc_result) {
+            case MMC_SUCCESS:
+                mmc_close(mmc);
+                return AACS_SUCCESS;
+            case MMC_ERROR_CERT_REVOKED:
+                error_code = AACS_ERROR_CERT_REVOKED;
+                break;
+            case MMC_ERROR:
+            default:
+                error_code = AACS_ERROR_MMC_FAILURE;
+                break;
+        }
+    }
+
+    mmc_close(mmc);
+
+    DEBUG(DBG_AACS, "Error reading read data key!\n");
+    return error_code;
+}
+
 static int _calc_vuk(AACS *aacs, uint8_t *mk, uint8_t *vuk,
                      pk_list *pkl, cert_list *host_cert_list)
 {
@@ -529,7 +577,7 @@ static void _find_config_entry(AACS *aacs, title_entry_list 
*ce,
         }
 }
 
-static int _calc_uks(AACS *aacs, const char *configfile_path)
+static int _calc_uks(AACS *aacs, config_file *cf)
 {
     AACS_FILE_H *fp = NULL;
     uint8_t  buf[16];
@@ -537,26 +585,18 @@ static int _calc_uks(AACS *aacs, const char 
*configfile_path)
     unsigned int i;
     int error_code;
 
-    config_file *cf = NULL;
     uint8_t mk[16] = {0}, vuk[16] = {0};
 
-    cf = keydbcfg_config_load(configfile_path);
-    if (!cf) {
-        return AACS_ERROR_NO_CONFIG;
-    }
-
     DEBUG(DBG_AACS, "Searching for keydb config entry...\n");
     _find_config_entry(aacs, cf->list, mk, vuk);
 
     /* Skip if retrieved from config file */
     if (aacs->uks) {
-        keydbcfg_config_file_close(cf);
         return AACS_SUCCESS;
     }
 
     /* Make sure we have VUK */
     error_code = _calc_vuk(aacs, mk, vuk, cf->pkl, cf->host_cert_list);
-    keydbcfg_config_file_close(cf);
     if (error_code != AACS_SUCCESS) {
         return error_code;
     }
@@ -770,6 +810,22 @@ static int _decrypt_unit(AACS *aacs, uint8_t *out_buf, 
const uint8_t *in_buf, ui
     return 0;
 }
 
+#define SECTOR_LEN 2048
+static void _decrypt_bus(AACS *aacs, uint8_t *out_buf, const uint8_t *in_buf)
+{
+    gcry_cipher_hd_t gcry_h;
+    uint8_t iv[] = "\x0b\xa0\xf8\xdd\xfe\xa6\x1f\xb3"
+                   "\xd8\xdf\x9f\x56\x6a\x05\x0f\x78";
+
+    memcpy(out_buf, in_buf, 16); /* first 16 bytes are plain */
+
+    gcry_cipher_open(&gcry_h, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_CBC, 0);
+    gcry_cipher_setkey(gcry_h, aacs->read_data_key, 16);
+    gcry_cipher_setiv(gcry_h, iv, 16);
+    gcry_cipher_decrypt(gcry_h, out_buf + 16, SECTOR_LEN - 16, in_buf + 16, 
SECTOR_LEN - 16);
+    gcry_cipher_close(gcry_h);
+}
+
 void aacs_get_version(int *major, int *minor, int *micro)
 {
     *major = AACS_VERSION_MAJOR;
@@ -803,6 +859,7 @@ AACS *aacs_open2(const char *path, const char 
*configfile_path, int *error_code)
     }
 
     AACS *aacs = calloc(1, sizeof(AACS));
+    config_file *cf;
 
     aacs->path = str_printf("%s", path);
 
@@ -812,18 +869,39 @@ AACS *aacs_open2(const char *path, const char 
*configfile_path, int *error_code)
         return NULL;
     }
 
+    cf = keydbcfg_config_load(configfile_path);
+    if (!cf) {
+        *error_code = AACS_ERROR_NO_CONFIG;
+        aacs_close(aacs);
+        return NULL;
+    }
+
     DEBUG(DBG_AACS, "Starting AACS waterfall...\n");
-    *error_code = _calc_uks(aacs, configfile_path);
+    *error_code = _calc_uks(aacs, cf);
+    if (*error_code != AACS_SUCCESS) {
+        DEBUG(DBG_AACS, "Failed to initialize AACS!\n");
+        keydbcfg_config_file_close(cf);
+        aacs_close(aacs);
+        return NULL;
+    }
 
     aacs->bee = _get_bus_encryption_enabled(path);
     aacs->bec = _get_bus_encryption_capable(path);
 
-    if (*error_code == AACS_SUCCESS) {
-        DEBUG(DBG_AACS, "AACS initialized!\n");
-    } else {
-        DEBUG(DBG_AACS, "Failed to initialize AACS!\n");
+    if (aacs->bee && aacs->bec) {
+        *error_code = _read_read_data_key(aacs, cf->host_cert_list);
+        if (*error_code != AACS_SUCCESS) {
+            DEBUG(DBG_AACS | DBG_CRIT, "Unable to initialize bus encryption 
required by drive and disc\n");
+            keydbcfg_config_file_close(cf);
+            aacs_close(aacs);
+            return NULL;
+        }
     }
 
+    keydbcfg_config_file_close(cf);
+
+    DEBUG(DBG_AACS, "AACS initialized!\n");
+
     return aacs;
 }
 
@@ -844,17 +922,24 @@ void aacs_close(AACS *aacs)
 int aacs_decrypt_unit(AACS *aacs, uint8_t *buf)
 {
     uint8_t out_buf[ALIGNED_UNIT_LEN];
+    int i;
 
     if (!(buf[0] & 0xc0)) {
         // TP_extra_header Copy_permission_indicator == 0, unit is not 
encrypted
         return 1;
     }
 
+    if (aacs->bee && aacs->bec) {
+        for (i = 0; i < ALIGNED_UNIT_LEN; i += SECTOR_LEN) {
+            _decrypt_bus(aacs, out_buf + i, buf + i);
+        }
+        memcpy(buf, out_buf, ALIGNED_UNIT_LEN);
+    }
+
     if (_decrypt_unit(aacs, out_buf, buf, aacs->current_cps_unit)) {
         memcpy(buf, out_buf, ALIGNED_UNIT_LEN);
 
         // Clear copy_permission_indicator bits
-        int i;
         for (i = 0; i < 6144; i += 192) {
             buf[i] &= ~0xc0;
         }
diff --git a/src/libaacs/mmc.c b/src/libaacs/mmc.c
index d6da2f4..2b76cea 100644
--- a/src/libaacs/mmc.c
+++ b/src/libaacs/mmc.c
@@ -978,10 +978,6 @@ MMC *mmc_open(const char *path)
     }
 #endif
 
-    if (mmc && mmc->bus_encryption) {
-        DEBUG(DBG_MMC | DBG_CRIT, "Bus encryption not implemented. Your drive 
requires bus encryption.\n");
-    }
-
     return mmc;
 }
 

_______________________________________________
libaacs-devel mailing list
libaacs-devel@videolan.org
https://mailman.videolan.org/listinfo/libaacs-devel

Reply via email to