[Ffmpeg-devel] [RFC] MXF AES decryption

Reimar Döffinger Reimar.Doeffinger
Thu Jan 11 00:07:15 CET 2007


Hello,
attached patch is just a demonstration. I'd like to get comments, and
preferably also some help for the actual implementation.
Problems:
1) The key is hardcoded (dkey variable). Is adding uint8_t *key, int
keylen to AVFormatContext an acceptable to "export" this?
2) Seeking does not work with encrypted files
3) SSL configure stuff is needed - or alternatively a standalone AES
implementation might be interesting as well...
4) Code needs quite a bit of general cleanup, it seems much too
complicated to me for what it does

Any volunteers to help out?

Greetings,
Reimar D?ffinger
-------------- next part --------------
Index: libavformat/mxf.c
===================================================================
--- libavformat/mxf.c	(revision 7437)
+++ libavformat/mxf.c	(working copy)
@@ -45,6 +45,7 @@
 
 //#define DEBUG
 
+#include <openssl/aes.h>
 #include "avformat.h"
 
 typedef uint8_t UID[16];
@@ -173,6 +174,7 @@
 /* partial keys to match */
 static const uint8_t mxf_header_partition_pack_key[]       = { 0x06,0x0e,0x2b,0x34,0x02,0x05,0x01,0x01,0x0d,0x01,0x02,0x01,0x01,0x02 };
 static const uint8_t mxf_essence_element_key[]             = { 0x06,0x0e,0x2b,0x34,0x01,0x02,0x01,0x01,0x0d,0x01,0x03,0x01 };
+static const uint8_t mxf_encrypted_triplet_key[]           = { 0x06,0x0e,0x2b,0x34,0x02,0x04,0x01,0x07,0x0d,0x01,0x03,0x01,0x02,0x7e,0x01,0x00 };
 
 #define IS_KLV_KEY(x, y) (!memcmp(x, y, sizeof(y)))
 
@@ -181,20 +183,8 @@
 
 static int64_t klv_decode_ber_length(ByteIOContext *pb)
 {
-    int64_t size = 0;
-    uint8_t length = get_byte(pb);
-    int type = length >> 7;
-
-    if (type) { /* long form */
-        int bytes_num = length & 0x7f;
-        /* SMPTE 379M 5.3.4 guarantee that bytes_num must not exceed 8 bytes */
-        if (bytes_num > 8)
-            return -1;
-        while (bytes_num--)
-            size = size << 8 | get_byte(pb);
-    } else {
-        size = length & 0x7f;
-    }
+    uint64_t size;
+    GET_BER(size, get_byte(pb), return -1;);
     return size;
 }
 
@@ -210,6 +200,8 @@
 {
     int i;
 
+    if (!IS_KLV_KEY(klv->key, mxf_essence_element_key))
+        return -1;
     for (i = 0; i < s->nb_streams; i++) {
         MXFTrack *track = s->streams[i]->priv_data;
          /* SMPTE 379M 7.3 */
@@ -228,8 +220,7 @@
 
     if (length > 61444) /* worst case PAL 1920 samples 8 channels */
         return -1;
-    get_buffer(pb, buffer, length);
-    av_new_packet(pkt, length);
+    memcpy(buffer, pkt->data, length);
     data_ptr = pkt->data;
     end_ptr = buffer + length;
     buf_ptr = buffer + 4; /* skip SMPTE 331M header */
@@ -249,6 +240,62 @@
     return 0;
 }
 
+static int get_enc_src_klv(AVFormatContext *s, AVPacket *pkt, KLVPacket *klv)
+{
+    static const uint8_t checkv[16] = {0x43, 0x48, 0x55, 0x4b, 0x43, 0x48, 0x55, 0x4b, 0x43, 0x48, 0x55, 0x4b, 0x43, 0x48, 0x55, 0x4b};
+    uint64_t size;
+    uint64_t source_sz;
+    uint64_t ptoff;
+    uint8_t *p = pkt->data;
+    uint8_t *end = &pkt->data[pkt->size];
+    char ivec[16];
+    uint8_t dkey[16] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+    AES_KEY key;
+    AES_set_decrypt_key(dkey, 128, &key);
+    // crypto context
+    if (&p[9 + 16] > end) return -1;
+    GET_BER(size, *p++ , return -1;);
+    if (size != 16) return -1;
+    p += size;
+    // plaintext offset
+    if (&p[9 + 8] > end) return -1;
+    GET_BER(size, *p++ , return -1;);
+    if (size != 8) return -1;
+    ptoff = (BE_32(p) << 32) | BE_32(p + 4);
+    p += size;
+    // source klv key
+    if (&p[9 + 16] > end) return -1;
+    GET_BER(size, *p++ , return -1;);
+    if (size != 16) return -1;
+    memcpy(&klv->key, p, 16);
+    p += 16;
+    // source size
+    if (&p[9 + 8] > end) return -1;
+    GET_BER(size, *p++ , return -1;);
+    if (size != 8) return -1;
+    source_sz = BE_32(p + 4);
+    p += size;
+    // enc. code
+    if (&p[9] > end) return -1;
+    GET_BER(size, *p++ , return -1;);
+    if (&p[size] > end || &p[size] < p) return -1;
+    if (size < 32) return -1;
+    memcpy(ivec, p, 16);
+    p += 16;
+    AES_cbc_encrypt(p, &pkt->data[ptoff], 16, &key, ivec, AES_DECRYPT);
+    if (memcmp(&pkt->data[ptoff], checkv, 16)) return -1;
+    p += 16;
+    size -= 32;
+    if (size < ptoff) return -1;
+    memcpy(pkt->data, p, ptoff);
+    size -= ptoff;
+    p += ptoff;
+    if (size < source_sz) return -1;
+    AES_cbc_encrypt(p, &pkt->data[ptoff], size, &key, ivec, AES_DECRYPT);
+    pkt->size = source_sz;
+    return 0;
+}
+
 static int mxf_read_packet(AVFormatContext *s, AVPacket *pkt)
 {
     KLVPacket klv;
@@ -261,21 +308,27 @@
 #ifdef DEBUG
         PRINT_KEY("read packet", klv.key);
 #endif
-        if (IS_KLV_KEY(klv.key, mxf_essence_element_key)) {
-            int index = mxf_get_stream_index(s, &klv);
+        if (IS_KLV_KEY(klv.key, mxf_essence_element_key) ||
+            IS_KLV_KEY(klv.key, mxf_encrypted_triplet_key)) {
+            int index;
+            av_get_packet(&s->pb, pkt, klv.length);
+            if (IS_KLV_KEY(klv.key, mxf_encrypted_triplet_key) &&
+                get_enc_src_klv(s, pkt, &klv) < 0)
+                av_log(s, AV_LOG_ERROR, "invalid encoded triplet\n");
+            index = mxf_get_stream_index(s, &klv);
             if (index < 0) {
                 av_log(s, AV_LOG_ERROR, "error getting stream index\n");
-                url_fskip(&s->pb, klv.length);
+                av_free_packet(pkt);
                 return -1;
             }
             /* check for 8 channels AES3 element */
             if (klv.key[12] == 0x06 && klv.key[13] == 0x01 && klv.key[14] == 0x10) {
                 if (mxf_get_d10_aes3_packet(&s->pb, s->streams[index], pkt, klv.length) < 0) {
                     av_log(s, AV_LOG_ERROR, "error reading D-10 aes3 frame\n");
+                    av_free_packet(pkt);
                     return -1;
                 }
-            } else
-                av_get_packet(&s->pb, pkt, klv.length);
+            }
             pkt->stream_index = index;
             return 0;
         } else
@@ -707,6 +760,7 @@
     { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x02,0x0D,0x01,0x03,0x01,0x02,0x04,0x60,0x01 }, CODEC_ID_MPEG2VIDEO, Frame }, /* MPEG-ES Frame wrapped */
     { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x02,0x0D,0x01,0x03,0x01,0x02,0x04,0xe0,0x02 }, CODEC_ID_MPEG2VIDEO,  Clip }, /* MPEG-ES Clip wrapped, 0xe0 MPV stream id */
     { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x04,0x61,0x07 }, CODEC_ID_MPEG2VIDEO,  Clip }, /* MPEG-ES Custom wrapped, 0x61 ??? stream id */
+    { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x07,0x0D,0x01,0x03,0x01,0x02,0x0b,0x01,0x00 }, CODEC_ID_MPEG2VIDEO, Frame }, /* MPEG-ES Frame wrapped, encrypted ??? */
     { { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },       CODEC_ID_NONE, Frame },
 };
 
@@ -982,7 +1036,8 @@
 #ifdef DEBUG
         PRINT_KEY("read header", klv.key);
 #endif
-        if (IS_KLV_KEY(klv.key, mxf_essence_element_key)) {
+        if (IS_KLV_KEY(klv.key, mxf_essence_element_key) ||
+            IS_KLV_KEY(klv.key, mxf_encrypted_triplet_key)) {
             /* FIXME avoid seek */
             url_fseek(&s->pb, klv.offset, SEEK_SET);
             break;
Index: libavutil/common.h
===================================================================
--- libavutil/common.h	(revision 7437)
+++ libavutil/common.h	(working copy)
@@ -201,6 +201,30 @@
 #define MKBETAG(a,b,c,d) (d | (c << 8) | (b << 16) | (a << 24))
 
 /*!
+ * \def GET_BER(val, GET_BYTE, ERROR)
+ * converts BER encoded integer (up to 8 bytes long) to its 64-bit value
+ * \param val is the output and should be of type uint64_t. It holds the converted
+ * 64 bit integer and should be a left value.
+ * \param GET_BYTE gets BER encoded bytes from any proper source. It can be
+ * a function or a statement whose return value or evaluated value is of type
+ * uint8_t. It will be executed up to 9 times.
+ * \param ERROR action that should be taken when an invalid BER byte is returned
+ * from GET_BYTE. It should be a statement that jumps out of the macro,
+ * like exit(), goto, return, break, or continue.
+ */
+#define GET_BER(val, GET_BYTE, ERROR)\
+    val= GET_BYTE;\
+    if (val & 0x80) { /* long form */\
+        int bytes_num = val & 0x7f;\
+        val = 0;\
+        /* SMPTE 379M 5.3.4 guarantee that bytes_num must not exceed 8 bytes */\
+        if (bytes_num > 8)\
+            ERROR\
+        while (bytes_num--)\
+            val = val << 8 | GET_BYTE;\
+    }
+
+/*!
  * \def GET_UTF8(val, GET_BYTE, ERROR)
  * converts a utf-8 character (up to 4 bytes long) to its 32-bit ucs-4 encoded form
  * \param val is the output and should be of type uint32_t. It holds the converted



More information about the ffmpeg-devel mailing list