]> git.sesse.net Git - ffmpeg/blobdiff - libavformat/mov.c
lavf/http: Implement server side network code.
[ffmpeg] / libavformat / mov.c
index 9d6b2e4f21634401500c70667d6764dfd00940f6..fdba34cf4a47a8b8bae3229a899ffdfeeae2f6b5 100644 (file)
@@ -37,6 +37,8 @@
 #include "libavutil/dict.h"
 #include "libavutil/display.h"
 #include "libavutil/opt.h"
+#include "libavutil/aes.h"
+#include "libavutil/sha.h"
 #include "libavutil/timecode.h"
 #include "libavcodec/ac3tab.h"
 #include "avformat.h"
@@ -807,6 +809,120 @@ static int mov_read_mdat(MOVContext *c, AVIOContext *pb, MOVAtom atom)
     return 0; /* now go for moov */
 }
 
+#define DRM_BLOB_SIZE 56
+
+static int mov_read_adrm(MOVContext *c, AVIOContext *pb, MOVAtom atom)
+{
+    uint8_t intermediate_key[20];
+    uint8_t intermediate_iv[20];
+    uint8_t input[64];
+    uint8_t output[64];
+    uint8_t file_checksum[20];
+    uint8_t calculated_checksum[20];
+    struct AVSHA *sha;
+    int i;
+    int ret = 0;
+    uint8_t *activation_bytes = c->activation_bytes;
+    uint8_t *fixed_key = c->audible_fixed_key;
+
+    c->aax_mode = 1;
+
+    sha = av_sha_alloc();
+    if (!sha)
+        return AVERROR(ENOMEM);
+    c->aes_decrypt = av_aes_alloc();
+    if (!c->aes_decrypt) {
+        ret = AVERROR(ENOMEM);
+        goto fail;
+    }
+
+    /* drm blob processing */
+    avio_read(pb, output, 8); // go to offset 8, absolute postion 0x251
+    avio_read(pb, input, DRM_BLOB_SIZE);
+    avio_read(pb, output, 4); // go to offset 4, absolute postion 0x28d
+    avio_read(pb, file_checksum, 20);
+
+    av_log(c->fc, AV_LOG_INFO, "[aax] file checksum == "); // required by external tools
+    for (i = 0; i < 20; i++)
+        av_log(sha, AV_LOG_INFO, "%02x", file_checksum[i]);
+    av_log(c->fc, AV_LOG_INFO, "\n");
+
+    /* verify activation data */
+    if (!activation_bytes || c->activation_bytes_size != 4) {
+        av_log(c->fc, AV_LOG_FATAL, "[aax] activation_bytes option is missing!\n");
+        ret = AVERROR(EINVAL);
+        goto fail;
+    }
+    if (c->activation_bytes_size != 4) {
+        av_log(c->fc, AV_LOG_FATAL, "[aax] activation_bytes value needs to be 4 bytes!\n");
+        ret = AVERROR(EINVAL);
+        goto fail;
+    }
+
+    /* verify fixed key */
+    if (c->audible_fixed_key_size != 16) {
+        av_log(c->fc, AV_LOG_FATAL, "[aax] audible_fixed_key value needs to be 16 bytes!\n");
+        ret = AVERROR(EINVAL);
+        goto fail;
+    }
+
+    /* AAX (and AAX+) key derivation */
+    av_sha_init(sha, 160);
+    av_sha_update(sha, fixed_key, 16);
+    av_sha_update(sha, activation_bytes, 4);
+    av_sha_final(sha, intermediate_key);
+    av_sha_init(sha, 160);
+    av_sha_update(sha, fixed_key, 16);
+    av_sha_update(sha, intermediate_key, 20);
+    av_sha_update(sha, activation_bytes, 4);
+    av_sha_final(sha, intermediate_iv);
+    av_sha_init(sha, 160);
+    av_sha_update(sha, intermediate_key, 16);
+    av_sha_update(sha, intermediate_iv, 16);
+    av_sha_final(sha, calculated_checksum);
+    if (memcmp(calculated_checksum, file_checksum, 20)) { // critical error
+        av_log(c->fc, AV_LOG_ERROR, "[aax] mismatch in checksums!\n");
+        ret = AVERROR_INVALIDDATA;
+        goto fail;
+    }
+    av_aes_init(c->aes_decrypt, intermediate_key, 128, 1);
+    av_aes_crypt(c->aes_decrypt, output, input, DRM_BLOB_SIZE >> 4, intermediate_iv, 1);
+    for (i = 0; i < 4; i++) {
+        // file data (in output) is stored in big-endian mode
+        if (activation_bytes[i] != output[3 - i]) { // critical error
+            av_log(c->fc, AV_LOG_ERROR, "[aax] error in drm blob decryption!\n");
+            ret = AVERROR_INVALIDDATA;
+            goto fail;
+        }
+    }
+    memcpy(c->file_key, output + 8, 16);
+    memcpy(input, output + 26, 16);
+    av_sha_init(sha, 160);
+    av_sha_update(sha, input, 16);
+    av_sha_update(sha, c->file_key, 16);
+    av_sha_update(sha, fixed_key, 16);
+    av_sha_final(sha, c->file_iv);
+
+fail:
+    av_free(sha);
+
+    return ret;
+}
+
+// Audible AAX (and AAX+) bytestream decryption
+static int aax_filter(uint8_t *input, int size, MOVContext *c)
+{
+    int blocks = 0;
+    unsigned char iv[16];
+
+    memcpy(iv, c->file_iv, 16); // iv is overwritten
+    blocks = size >> 4; // trailing bytes are not encrypted!
+    av_aes_init(c->aes_decrypt, c->file_key, 128, 1);
+    av_aes_crypt(c->aes_decrypt, input, input, blocks, iv, 1);
+
+    return 0;
+}
+
 /* read major brand, minor version and compatible brands and store them as metadata */
 static int mov_read_ftyp(MOVContext *c, AVIOContext *pb, MOVAtom atom)
 {
@@ -1130,14 +1246,14 @@ static int mov_read_fiel(MOVContext *c, AVIOContext *pb, MOVAtom atom)
 static int mov_realloc_extradata(AVCodecContext *codec, MOVAtom atom)
 {
     int err = 0;
-    uint64_t size = (uint64_t)codec->extradata_size + atom.size + 8 + FF_INPUT_BUFFER_PADDING_SIZE;
+    uint64_t size = (uint64_t)codec->extradata_size + atom.size + 8 + AV_INPUT_BUFFER_PADDING_SIZE;
     if (size > INT_MAX || (uint64_t)atom.size > INT_MAX)
         return AVERROR_INVALIDDATA;
     if ((err = av_reallocp(&codec->extradata, size)) < 0) {
         codec->extradata_size = 0;
         return err;
     }
-    codec->extradata_size = size - FF_INPUT_BUFFER_PADDING_SIZE;
+    codec->extradata_size = size - AV_INPUT_BUFFER_PADDING_SIZE;
     return 0;
 }
 
@@ -1159,7 +1275,7 @@ static int64_t mov_read_atom_into_extradata(MOVContext *c, AVIOContext *pb, MOVA
         codec->extradata_size -= atom.size - err;
         result = err;
     }
-    memset(buf + 8 + err, 0, FF_INPUT_BUFFER_PADDING_SIZE);
+    memset(buf + 8 + err, 0, AV_INPUT_BUFFER_PADDING_SIZE);
     return result;
 }
 
@@ -1783,7 +1899,7 @@ static int mov_rewrite_dvd_sub_extradata(AVStream *st)
 
     av_freep(&st->codec->extradata);
     st->codec->extradata_size = 0;
-    st->codec->extradata = av_mallocz(strlen(buf) + FF_INPUT_BUFFER_PADDING_SIZE);
+    st->codec->extradata = av_mallocz(strlen(buf) + AV_INPUT_BUFFER_PADDING_SIZE);
     if (!st->codec->extradata)
         return AVERROR(ENOMEM);
     st->codec->extradata_size = strlen(buf);
@@ -1811,7 +1927,7 @@ static int mov_parse_stsd_data(MOVContext *c, AVIOContext *pb,
             val = AV_RB32(st->codec->extradata + 4);
             tmcd_ctx->tmcd_flags = val;
             if (val & 1)
-                st->codec->flags2 |= CODEC_FLAG2_DROP_FRAME_TIMECODE;
+                st->codec->flags2 |= AV_CODEC_FLAG2_DROP_FRAME_TIMECODE;
             st->codec->time_base.den = st->codec->extradata[16]; /* number of frame */
             st->codec->time_base.num = 1;
             /* adjust for per frame dur in counter mode */
@@ -2220,7 +2336,7 @@ static int mov_read_stsz(MOVContext *c, AVIOContext *pb, MOVAtom atom)
 
     num_bytes = (entries*field_size+4)>>3;
 
-    buf = av_malloc(num_bytes+FF_INPUT_BUFFER_PADDING_SIZE);
+    buf = av_malloc(num_bytes+AV_INPUT_BUFFER_PADDING_SIZE);
     if (!buf) {
         av_freep(&sc->sample_sizes);
         return AVERROR(ENOMEM);
@@ -3637,6 +3753,7 @@ static const MOVParseTableEntry mov_default_parse_table[] = {
 { MKTAG('e','l','s','t'), mov_read_elst },
 { MKTAG('e','n','d','a'), mov_read_enda },
 { MKTAG('f','i','e','l'), mov_read_fiel },
+{ MKTAG('a','d','r','m'), mov_read_adrm },
 { MKTAG('f','t','y','p'), mov_read_ftyp },
 { MKTAG('g','l','b','l'), mov_read_glbl },
 { MKTAG('h','d','l','r'), mov_read_hdlr },
@@ -4058,6 +4175,8 @@ static int mov_read_close(AVFormatContext *s)
     }
     av_freep(&mov->fragment_index_data);
 
+    av_freep(&mov->aes_decrypt);
+
     return 0;
 }
 
@@ -4477,6 +4596,9 @@ static int mov_read_packet(AVFormatContext *s, AVPacket *pkt)
     pkt->flags |= sample->flags & AVINDEX_KEYFRAME ? AV_PKT_FLAG_KEY : 0;
     pkt->pos = sample->pos;
 
+    if (mov->aax_mode)
+        aax_filter(pkt->data, pkt->size, mov);
+
     return 0;
 }
 
@@ -4590,6 +4712,12 @@ static const AVOption mov_options[] = {
         AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, .flags = FLAGS },
     { "export_xmp", "Export full XMP metadata", OFFSET(export_xmp),
         AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, .flags = FLAGS },
+    { "activation_bytes", "Secret bytes for Audible AAX files", OFFSET(activation_bytes),
+        AV_OPT_TYPE_BINARY, .flags = AV_OPT_FLAG_DECODING_PARAM },
+    { "audible_fixed_key", // extracted from libAAX_SDK.so and AAXSDKWin.dll files!
+        "Fixed key used for handling Audible AAX files", OFFSET(audible_fixed_key),
+        AV_OPT_TYPE_BINARY, {.str="77214d4b196a87cd520045fd20a51d67"},
+        .flags = AV_OPT_FLAG_DECODING_PARAM },
     { NULL },
 };