#include "libavutil/display.h"
#include "libavutil/opt.h"
#include "libavutil/aes.h"
+#include "libavutil/aes_ctr.h"
#include "libavutil/sha.h"
#include "libavutil/timecode.h"
#include "libavcodec/ac3tab.h"
sc->pseudo_stream_id = st->codec->codec_tag ? -1 : pseudo_stream_id;
sc->dref_id= dref_id;
+ sc->format = format;
id = mov_codec_id(st, format);
return 0;
}
+static int mov_read_frma(MOVContext *c, AVIOContext *pb, MOVAtom atom)
+{
+ uint32_t format = avio_rl32(pb);
+ MOVStreamContext *sc;
+ enum AVCodecID id;
+ AVStream *st;
+
+ if (c->fc->nb_streams < 1)
+ return 0;
+ st = c->fc->streams[c->fc->nb_streams - 1];
+ sc = st->priv_data;
+
+ switch (sc->format)
+ {
+ case MKTAG('e','n','c','v'): // encrypted video
+ case MKTAG('e','n','c','a'): // encrypted audio
+ id = mov_codec_id(st, format);
+ if (st->codec->codec_id != AV_CODEC_ID_NONE &&
+ st->codec->codec_id != id) {
+ av_log(c->fc, AV_LOG_WARNING,
+ "ignoring 'frma' atom of '%.4s', stream has codec id %d\n",
+ (char*)&format, st->codec->codec_id);
+ break;
+ }
+
+ st->codec->codec_id = id;
+ sc->format = format;
+ break;
+
+ default:
+ av_log(c->fc, AV_LOG_WARNING,
+ "ignoring 'frma' atom of '%.4s', stream format is '%.4s'\n",
+ (char*)&format, (char*)&sc->format);
+ break;
+ }
+
+ return 0;
+}
+
+static int mov_read_senc(MOVContext *c, AVIOContext *pb, MOVAtom atom)
+{
+ AVStream *st;
+ MOVStreamContext *sc;
+ size_t auxiliary_info_size;
+
+ if (c->decryption_key_len == 0 || c->fc->nb_streams < 1)
+ return 0;
+
+ st = c->fc->streams[c->fc->nb_streams - 1];
+ sc = st->priv_data;
+
+ if (sc->cenc.aes_ctr) {
+ av_log(c->fc, AV_LOG_ERROR, "duplicate senc atom\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ avio_r8(pb); /* version */
+ sc->cenc.use_subsamples = avio_rb24(pb) & 0x02; /* flags */
+
+ avio_rb32(pb); /* entries */
+
+ if (atom.size < 8) {
+ av_log(c->fc, AV_LOG_ERROR, "senc atom size %"PRId64" too small\n", atom.size);
+ return AVERROR_INVALIDDATA;
+ }
+
+ /* save the auxiliary info as is */
+ auxiliary_info_size = atom.size - 8;
+
+ sc->cenc.auxiliary_info = av_malloc(auxiliary_info_size);
+ if (!sc->cenc.auxiliary_info) {
+ return AVERROR(ENOMEM);
+ }
+
+ sc->cenc.auxiliary_info_end = sc->cenc.auxiliary_info + auxiliary_info_size;
+
+ sc->cenc.auxiliary_info_pos = sc->cenc.auxiliary_info;
+
+ if (avio_read(pb, sc->cenc.auxiliary_info, auxiliary_info_size) != auxiliary_info_size) {
+ av_log(c->fc, AV_LOG_ERROR, "failed to read the auxiliary info");
+ return AVERROR_INVALIDDATA;
+ }
+
+ /* initialize the cipher */
+ sc->cenc.aes_ctr = av_aes_ctr_alloc();
+ if (!sc->cenc.aes_ctr) {
+ return AVERROR(ENOMEM);
+ }
+
+ return av_aes_ctr_init(sc->cenc.aes_ctr, c->decryption_key);
+}
+
+static int cenc_filter(MOVContext *c, MOVStreamContext *sc, uint8_t *input, int size)
+{
+ uint32_t encrypted_bytes;
+ uint16_t subsample_count;
+ uint16_t clear_bytes;
+ uint8_t* input_end = input + size;
+
+ /* read the iv */
+ if (AES_CTR_IV_SIZE > sc->cenc.auxiliary_info_end - sc->cenc.auxiliary_info_pos) {
+ av_log(c->fc, AV_LOG_ERROR, "failed to read iv from the auxiliary info\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ av_aes_ctr_set_iv(sc->cenc.aes_ctr, sc->cenc.auxiliary_info_pos);
+ sc->cenc.auxiliary_info_pos += AES_CTR_IV_SIZE;
+
+ if (!sc->cenc.use_subsamples)
+ {
+ /* decrypt the whole packet */
+ av_aes_ctr_crypt(sc->cenc.aes_ctr, input, input, size);
+ return 0;
+ }
+
+ /* read the subsample count */
+ if (sizeof(uint16_t) > sc->cenc.auxiliary_info_end - sc->cenc.auxiliary_info_pos) {
+ av_log(c->fc, AV_LOG_ERROR, "failed to read subsample count from the auxiliary info\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ subsample_count = AV_RB16(sc->cenc.auxiliary_info_pos);
+ sc->cenc.auxiliary_info_pos += sizeof(uint16_t);
+
+ for (; subsample_count > 0; subsample_count--)
+ {
+ if (6 > sc->cenc.auxiliary_info_end - sc->cenc.auxiliary_info_pos) {
+ av_log(c->fc, AV_LOG_ERROR, "failed to read subsample from the auxiliary info\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ /* read the number of clear / encrypted bytes */
+ clear_bytes = AV_RB16(sc->cenc.auxiliary_info_pos);
+ sc->cenc.auxiliary_info_pos += sizeof(uint16_t);
+ encrypted_bytes = AV_RB32(sc->cenc.auxiliary_info_pos);
+ sc->cenc.auxiliary_info_pos += sizeof(uint32_t);
+
+ if ((uint64_t)clear_bytes + encrypted_bytes > input_end - input) {
+ av_log(c->fc, AV_LOG_ERROR, "subsample size exceeds the packet size left\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ /* skip the clear bytes */
+ input += clear_bytes;
+
+ /* decrypt the encrypted bytes */
+ av_aes_ctr_crypt(sc->cenc.aes_ctr, input, input, encrypted_bytes);
+ input += encrypted_bytes;
+ }
+
+ if (input < input_end) {
+ av_log(c->fc, AV_LOG_ERROR, "leftover packet bytes after subsample processing\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ return 0;
+}
+
static const MOVParseTableEntry mov_default_parse_table[] = {
{ MKTAG('A','C','L','R'), mov_read_aclr },
{ MKTAG('A','P','R','G'), mov_read_avid },
{ MKTAG('C','i','n', 0x8e), mov_read_targa_y216 },
{ MKTAG('f','r','e','e'), mov_read_free },
{ MKTAG('-','-','-','-'), mov_read_custom },
+{ MKTAG('s','i','n','f'), mov_read_default },
+{ MKTAG('f','r','m','a'), mov_read_frma },
+{ MKTAG('s','e','n','c'), mov_read_senc },
{ 0, NULL }
};
av_freep(&sc->elst_data);
av_freep(&sc->rap_group);
av_freep(&sc->display_matrix);
+
+ av_freep(&sc->cenc.auxiliary_info);
+ av_aes_ctr_free(sc->cenc.aes_ctr);
}
if (mov->dv_demux) {
MOVAtom atom = { AV_RL32("root") };
int i;
+ if (mov->decryption_key_len != 0 && mov->decryption_key_len != AES_CTR_KEY_SIZE) {
+ av_log(s, AV_LOG_ERROR, "Invalid decryption key len %d expected %d\n",
+ mov->decryption_key_len, AES_CTR_KEY_SIZE);
+ return AVERROR(EINVAL);
+ }
+
mov->fc = s;
mov->trak_index = -1;
/* .mov and .mp4 aren't streamable anyway (only progressive download if moov is before mdat) */
if (mov->aax_mode)
aax_filter(pkt->data, pkt->size, mov);
+ if (sc->cenc.aes_ctr) {
+ ret = cenc_filter(mov, sc, pkt->data, pkt->size);
+ if (ret) {
+ return ret;
+ }
+ }
+
return 0;
}
"Fixed key used for handling Audible AAX files", OFFSET(audible_fixed_key),
AV_OPT_TYPE_BINARY, {.str="77214d4b196a87cd520045fd20a51d67"},
.flags = AV_OPT_FLAG_DECODING_PARAM },
+ { "decryption_key", "The media decryption key (hex)", OFFSET(decryption_key), AV_OPT_TYPE_BINARY, .flags = AV_OPT_FLAG_DECODING_PARAM },
+
{ NULL },
};