[Ffmpeg-devel] [RFC] MXF AES decryption

Reimar Döffinger Reimar.Doeffinger
Sun Jan 14 17:25:51 CET 2007


Hello,
another little update.
On Sat, Jan 13, 2007 at 06:24:30PM +0100, Michael Niedermayer wrote:
> On Sat, Jan 13, 2007 at 02:13:37PM +0100, Reimar D?ffinger wrote:
[...]
> > The key in AVFormatParameters is supposed to be a string for easier
> > extensibility and easy way to specify on commandline mostly.
> 
> iam unhappy with this, what about per stream keys? and why not simply
> pass a uint8_t array? also i hate AVFormatParameters why not use 
> AVFormatContext ?

Per stream keys: I'd like to ignore for now, they could be handled via
AVStream, but
1) there also needs to be a way to associate keys to
streams, so e.g. the key ID would have to be exported, too
2) A key somewhere else would probably still be needed, since it would
not work for formats where either already the header parsing function
needs a key and not work well for formats where AVStreams are added
during playback.
But I moved it to AVFormatContext in binary format with additional
keylen field.

Updated patch also should finally really fix seeking for encrypted
files.

> > The current format is just a hex string like "02045a...", 32 characters for
> > AES-128 (the only format supported currently).
> > IMO openssl should be replaced, it is too bloated for such a simple
> > functionality but I'm not yet sure by what, not to mention that I am not
> > up to date if it still has such an inconvenient license...
> 
> <random bloated crpto lib> dependance for just AES is completely unacceptable
> write your own 2 page implementation of AES

It also uses now my aes128 code, which can also be switched to libgcrypt
for whoever needs that final bit of speed our code does not yet provide.

Greetings,
Reimar D?ffinger
-------------- next part --------------
Index: libavformat/avformat.h
===================================================================
--- libavformat/avformat.h	(revision 7475)
+++ libavformat/avformat.h	(working copy)
@@ -25,8 +25,8 @@
 extern "C" {
 #endif
 
-#define LIBAVFORMAT_VERSION_INT ((51<<16)+(7<<8)+0)
-#define LIBAVFORMAT_VERSION     51.7.0
+#define LIBAVFORMAT_VERSION_INT ((51<<16)+(8<<8)+0)
+#define LIBAVFORMAT_VERSION     51.8.0
 #define LIBAVFORMAT_BUILD       LIBAVFORMAT_VERSION_INT
 
 #define LIBAVFORMAT_IDENT       "Lavf" AV_STRINGIFY(LIBAVFORMAT_VERSION)
@@ -361,6 +352,8 @@
     int loop_input;
     /* decoding: size of data to probe; encoding unused */
     unsigned int probesize;
+    const char *key;
+    int keylen;
 } AVFormatContext;
 
 typedef struct AVPacketList {
Index: libavformat/mxf.c
===================================================================
--- libavformat/mxf.c	(revision 7475)
+++ libavformat/mxf.c	(working copy)
@@ -46,6 +46,7 @@
 //#define DEBUG
 
 #include "avformat.h"
+#include "aes128.h"
 
 typedef uint8_t UID[16];
 
@@ -60,6 +61,9 @@
     Descriptor,
     Track,
     EssenceContainerData,
+    CryptoFramework,
+    CryptoContext,
+    DMSegment,
 };
 
 typedef struct MXFStructuralComponent {
@@ -125,6 +129,27 @@
     UID linked_package_uid;
 } MXFEssenceContainerData;
 
+typedef struct MXFDMSegment {
+    UID uid;
+    enum MXFMetadataSetType type;
+    int *trackids;
+    int trackids_count;
+    UID framework_ul;
+} MXFDMSegment;
+
+typedef struct MXFCryptoFramework {
+    UID uid;
+    enum MXFMetadataSetType type;
+    UID cryptocontext;
+} MXFCryptoFramework;
+
+typedef struct MXFCryptoContext {
+    UID uid;
+    enum MXFMetadataSetType type;
+    UID contextid;
+    UID source_ul;
+} MXFCryptoContext;
+
 typedef struct {
     UID uid;
     enum MXFMetadataSetType type;
@@ -141,7 +166,9 @@
     UID content_storage_uid;
     MXFMetadataSet **metadata_sets;
     int metadata_sets_count;
+    UID *sync_key;
     AVFormatContext *fc;
+    AES128Context *aesc;
 } MXFContext;
 
 typedef struct KLVPacket {
@@ -174,6 +201,9 @@
 /* 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 };
+/* complete keys to match */
+static const uint8_t mxf_encrypted_triplet_key[]           = { 0x06,0x0e,0x2b,0x34,0x02,0x04,0x01,0x07,0x0d,0x01,0x03,0x01,0x02,0x7e,0x01,0x00 };
+static const uint8_t mxf_encrypted_essence_container[]     = { 0x06,0x0e,0x2b,0x34,0x04,0x01,0x01,0x07,0x0d,0x01,0x03,0x01,0x02,0x0b,0x01,0x00 };
 
 #define IS_KLV_KEY(x, y) (!memcmp(x, y, sizeof(y)))
 
@@ -207,6 +237,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 */
@@ -225,8 +257,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 */
@@ -246,11 +277,58 @@
     return 0;
 }
 
+static int mxf_decrypt_triplet(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};
+    MXFContext *mxf = s->priv_data;
+    ByteIOContext *pb = &s->pb;
+    offset_t end = url_ftell(pb) + klv->length;
+    uint64_t size;
+    uint64_t orig_size;
+    uint64_t plaintext_size;
+    uint8_t ivec[16];
+    uint8_t tmpbuf[16];
+    if (s->key && s->keylen == 16)
+        aes128_set_key(mxf->aesc, s->key);
+    // crypto context
+    url_fskip(pb, klv_decode_ber_length(pb));
+    // plaintext offset
+    klv_decode_ber_length(pb);
+    plaintext_size = get_be64(pb);
+    // source klv key
+    klv_decode_ber_length(pb);
+    get_buffer(pb, klv->key, 16);
+    // source size
+    klv_decode_ber_length(pb);
+    orig_size = get_be64(pb);
+    if (orig_size < plaintext_size) goto err_out;
+    // enc. code
+    size = klv_decode_ber_length(pb);
+    if (size < 32 || size - 32 < orig_size) goto err_out;
+    get_buffer(pb, ivec, 16);
+    get_buffer(pb, tmpbuf, 16);
+    aes128_cbc_decrypt(mxf->aesc, tmpbuf, 1, ivec);
+    if (memcmp(tmpbuf, checkv, 16))
+        av_log(s, AV_LOG_ERROR, "probably incorrect decryption key\n");
+    size -= 32;
+    av_get_packet(pb, pkt, size);
+    size -= plaintext_size;
+    aes128_cbc_decrypt(mxf->aesc, &pkt->data[plaintext_size], size >> 4, ivec);
+    pkt->size = orig_size;
+    url_fskip(pb, end - url_ftell(pb));
+    return 0;
+
+err_out:
+    url_fskip(pb, end - url_ftell(pb));
+    return -1;
+}
+
 static int mxf_read_packet(AVFormatContext *s, AVPacket *pkt)
 {
     KLVPacket klv;
 
     while (!url_feof(&s->pb)) {
+        int encrypted = 0;
         if (klv_read_packet(&klv, &s->pb) < 0) {
             av_log(s, AV_LOG_ERROR, "error reading KLV packet\n");
             return -1;
@@ -258,21 +336,29 @@
 #ifdef DEBUG
         PRINT_KEY("read packet", klv.key);
 #endif
+        if (IS_KLV_KEY(klv.key, mxf_encrypted_triplet_key)) {
+            int res = mxf_decrypt_triplet(s, pkt, &klv);
+            if (res < 0) {
+                av_log(s, AV_LOG_ERROR, "invalid encoded triplet\n");
+                continue;
+            }
+            encrypted = 1;
+        }
         if (IS_KLV_KEY(klv.key, mxf_essence_element_key)) {
             int 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);
                 return -1;
             }
+            if (!encrypted)
+                av_get_packet(&s->pb, pkt, klv.length);
             /* 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");
                     return -1;
                 }
-            } else
-                av_get_packet(&s->pb, pkt, klv.length);
+            }
             pkt->stream_index = index;
             return 0;
         } else
@@ -562,6 +648,43 @@
 MXF_READ_LOCAL_TAGS_STOP(Descriptor, MXFDescriptor, descriptor)
 }
 
+static int mxf_read_metadata_dmsegment(MXFContext *mxf, KLVPacket *klv)
+{
+MXF_READ_LOCAL_TAGS_START(DMSegment, MXFDMSegment, dmsegment)
+        case 0x6101:
+            get_buffer(pb, dmsegment->framework_ul, 16);
+            break;
+        case 0x6102:
+            dmsegment->trackids_count = get_be32(pb);
+            if (dmsegment->trackids_count >= UINT_MAX / sizeof(int))
+                return -1;
+            dmsegment->trackids = av_malloc(dmsegment->trackids_count * sizeof(int));
+            url_fskip(pb, 4); /* useless size of objects, always 4 according to specs */
+            get_buffer(pb, (uint8_t *)dmsegment->trackids, dmsegment->trackids_count * sizeof(int));
+            break;
+MXF_READ_LOCAL_TAGS_STOP(DMSegment, MXFDMSegment, dmsegment)
+}
+static int mxf_read_metadata_cryptographic_framework(MXFContext *mxf, KLVPacket *klv)
+{
+MXF_READ_LOCAL_TAGS_START(CryptoFramework, MXFCryptoFramework, cryptoframework)
+        case 0xFFFF:
+            get_buffer(pb, cryptoframework->cryptocontext, 16);
+            break;
+MXF_READ_LOCAL_TAGS_STOP(CryptoFramework, MXFCryptoFramework, cryptoframework)
+}
+
+static int mxf_read_metadata_cryptographic_context(MXFContext *mxf, KLVPacket *klv)
+{
+MXF_READ_LOCAL_TAGS_START(CryptoContext, MXFCryptoContext, cryptocontext)
+        case 0xFFFE:
+            get_buffer(pb, cryptocontext->contextid, 16);
+            break;
+        case 0xFFFD:
+            get_buffer(pb, cryptocontext->source_ul, 16);
+            break;
+MXF_READ_LOCAL_TAGS_STOP(CryptoContext, MXFCryptoContext, cryptocontext)
+}
+
 /* SMPTE RP224 http://www.smpte-ra.org/mdd/index.html */
 static const MXFDataDefinitionUL mxf_data_definition_uls[] = {
     { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x01,0x03,0x02,0x02,0x01,0x00,0x00,0x00 }, CODEC_TYPE_VIDEO },
@@ -652,6 +775,27 @@
     return NULL;
 }
 
+static MXFCryptoContext *mxf_find_track_cryptocontext(MXFContext *mxf, int track_id)
+{
+    int i, j;
+    for (i = 0; i < mxf->metadata_sets_count; i++) {
+        if (mxf->metadata_sets[i]->type == DMSegment) {
+            MXFDMSegment *seg = (MXFDMSegment *)mxf->metadata_sets[i];
+            for (j = 0; j < seg->trackids_count; j++)
+                if (seg->trackids[j] == track_id) break;
+            if (j < seg->trackids_count || seg->trackids_count == 0) {
+                MXFCryptoContext *cc = NULL;
+                MXFCryptoFramework *cf = mxf_resolve_strong_ref(mxf, seg->framework_ul, CryptoFramework);
+                if (cf)
+                    cc = mxf_resolve_strong_ref(mxf, cf->cryptocontext, CryptoContext);
+                if (cc)
+                    return cc;
+            }
+        }
+    }
+    return NULL;
+}
+
 static int mxf_parse_structural_metadata(MXFContext *mxf)
 {
     MXFPackage *material_package = NULL;
@@ -679,6 +823,7 @@
         const MXFCodecUL *codec_ul = NULL;
         const MXFCodecUL *container_ul = NULL;
         AVStream *st;
+        UID *essence_container_ul;
 
         if (!(material_track = mxf_resolve_strong_ref(mxf, &material_package->tracks_refs[i], Track))) {
             av_log(mxf->fc, AV_LOG_ERROR, "could not resolve material track strong ref\n");
@@ -768,9 +913,16 @@
             av_log(mxf->fc, AV_LOG_INFO, "source track %d: stream %d, no descriptor found\n", source_track->track_id, st->index);
             continue;
         }
+        essence_container_ul = descriptor->essence_container_ul;
+        if (IS_KLV_KEY(essence_container_ul, mxf_encrypted_essence_container)) {
+           MXFCryptoContext *cc = mxf_find_track_cryptocontext(mxf, descriptor->linked_track_id);
+           if (cc)
+               essence_container_ul = cc->source_ul;
+           mxf->sync_key = mxf_encrypted_triplet_key;
+        }
 #ifdef DEBUG
         PRINT_KEY("essence codec     ul", descriptor->essence_codec_ul);
-        PRINT_KEY("essence container ul", descriptor->essence_container_ul);
+        PRINT_KEY("essence container ul", essence_container_ul);
 #endif
         /* TODO: drop PictureEssenceCoding and SoundEssenceCompression, only check EssenceContainer */
         codec_ul = mxf_get_codec_ul(mxf_codec_uls, &descriptor->essence_codec_ul);
@@ -780,7 +932,7 @@
             st->codec->extradata_size = descriptor->extradata_size;
         }
         if (st->codec->codec_type == CODEC_TYPE_VIDEO) {
-            container_ul = mxf_get_codec_ul(mxf_picture_essence_container_uls, &descriptor->essence_container_ul);
+            container_ul = mxf_get_codec_ul(mxf_picture_essence_container_uls, essence_container_ul);
             if (st->codec->codec_id == CODEC_ID_NONE)
                 st->codec->codec_id = container_ul->id;
             st->codec->width = descriptor->width;
@@ -788,7 +940,7 @@
             st->codec->bits_per_sample = descriptor->bits_per_sample; /* Uncompressed */
             st->need_parsing = 2; /* only parse headers */
         } else if (st->codec->codec_type == CODEC_TYPE_AUDIO) {
-            container_ul = mxf_get_codec_ul(mxf_sound_essence_container_uls, &descriptor->essence_container_ul);
+            container_ul = mxf_get_codec_ul(mxf_sound_essence_container_uls, essence_container_ul);
             if (st->codec->codec_id == CODEC_ID_NONE)
                 st->codec->codec_id = container_ul->id;
             st->codec->channels = descriptor->channels;
@@ -835,6 +987,9 @@
     { { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x47,0x00 }, mxf_read_metadata_generic_descriptor }, /* AES3 */
     { { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x3A,0x00 }, mxf_read_metadata_track }, /* Static Track */
     { { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x3B,0x00 }, mxf_read_metadata_track }, /* Generic Track */
+    { { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x41,0x00 }, mxf_read_metadata_dmsegment },
+    { { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x04,0x01,0x02,0x01,0x00,0x00 }, mxf_read_metadata_cryptographic_framework },
+    { { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x04,0x01,0x02,0x02,0x00,0x00 }, mxf_read_metadata_cryptographic_context },
     { { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, NULL },
 };
 
@@ -856,6 +1011,8 @@
     MXFContext *mxf = s->priv_data;
     KLVPacket klv;
 
+    mxf->aesc = aes128_init();
+    mxf->sync_key = mxf_essence_element_key;
     if (!mxf_read_sync(&s->pb, mxf_header_partition_pack_key, 14)) {
         av_log(s, AV_LOG_ERROR, "could not find header partition pack key\n");
         return -1;
@@ -872,7 +1029,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;
@@ -913,12 +1071,16 @@
         case MaterialPackage:
             av_freep(&((MXFPackage *)mxf->metadata_sets[i])->tracks_refs);
             break;
+        case DMSegment:
+            av_freep(&((MXFDMSegment *)mxf->metadata_sets[i])->trackids);
+            break;
         default:
             break;
         }
         av_freep(&mxf->metadata_sets[i]);
     }
     av_freep(&mxf->metadata_sets);
+    av_freep(&mxf->aesc);
     return 0;
 }
 
@@ -942,6 +1104,7 @@
 /* XXX: use MXF Index */
 static int mxf_read_seek(AVFormatContext *s, int stream_index, int64_t sample_time, int flags)
 {
+    MXFContext *mxf = s->priv_data;
     AVStream *st = s->streams[stream_index];
     int64_t seconds;
 
@@ -951,7 +1114,7 @@
         sample_time = 0;
     seconds = av_rescale(sample_time, st->time_base.num, st->time_base.den);
     url_fseek(&s->pb, (s->bit_rate * seconds) >> 3, SEEK_SET);
-    if (!mxf_read_sync(&s->pb, mxf_essence_element_key, 12))
+    if (!mxf_read_sync(&s->pb, mxf->sync_key, 12))
         return -1;
 
     /* found KLV key */



More information about the ffmpeg-devel mailing list