[Ffmpeg-devel] [RFC] MXF AES decryption

Baptiste Coudurier baptiste.coudurier
Sun Jan 14 19:26:25 CET 2007


Hi

Reimar D?ffinger wrote:
> 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
> 
> 
> ------------------------------------------------------------------------
> 
> 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;

Why not uint8_t * ?

> [...[
> @@ -141,7 +166,9 @@
>      UID content_storage_uid;
>      MXFMetadataSet **metadata_sets;
>      int metadata_sets_count;
> +    UID *sync_key;
>      AVFormatContext *fc;

Should not sync_key be uint8_t * ?

>  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 @@

Why is that hunk needed ? dont call get_stream_index if key is not
mxf_essence_element_key

>      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_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)
>  }

I would prefer calling mxf_get_stream_index from klv_decrypt_triplet,
and avoid using that encrypted var in the mail loop.
Also avoid one useless memcpy for unencrypted files and d10_aes3 stream
and that code should be to an AVBitstreamFilter anyway.

> [...]
>
>  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;
> +        }

That's wrong. descriptor essence container should be original source
coutainer. That file is broken. Working 100% solution must be
implemented, also linked track id is optional in S377M. Yes it is
complicated but a working 100% solution exists.

> [...]
>
> @@ -856,6 +1011,8 @@
>      MXFContext *mxf = s->priv_data;
>      KLVPacket klv;
>  
> +    mxf->aesc = aes128_init();

Init must be optional of course.

>      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;

We could use header size in partition pack and stop if end is reached.
That should avoid seek and check for keys.

> [...]
>  
> @@ -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 */
> 

Seek hunk looks ok.

-- 
Baptiste COUDURIER                              GnuPG Key Id: 0x5C1ABAAA
SMARTJOG S.A.                                    http://www.smartjog.com
Key fingerprint                 8D77134D20CC9220201FC5DB0AC9325C5C1ABAAA
Phone: +33 1 49966312




More information about the ffmpeg-devel mailing list