summaryrefslogtreecommitdiff
path: root/libavformat/mov.c
diff options
context:
space:
mode:
authorNachiket Tarate <nachiket.programmer@gmail.com>2021-09-22 00:11:31 +0530
committerSteven Liu <lq@chinaffmpeg.org>2021-10-13 11:23:44 +0800
commitef0f5d1be67a97bf8a5c3da07e12f55b34a24ec3 (patch)
treeca4814cf7b87f996aff15a011956816e459c3748 /libavformat/mov.c
parent343c03c61cdf311f5eebb43731a3e5fd116200dc (diff)
libavformat/mov: add support for 'cens', 'cbc1' and 'cbcs' encryption schemes specified in Common Encryption (CENC) standard
correct implementation of 'cenc' encryption scheme to support decryption of partial cipher blocks at the end of subsamples https://www.iso.org/standard/68042.html Signed-off-by: Nachiket Tarate <nachiket.programmer@gmail.com> Signed-off-by: Steven Liu <lq@chinaffmpeg.org>
Diffstat (limited to 'libavformat/mov.c')
-rw-r--r--libavformat/mov.c245
1 files changed, 239 insertions, 6 deletions
diff --git a/libavformat/mov.c b/libavformat/mov.c
index a811bc7677..57c67e3aac 100644
--- a/libavformat/mov.c
+++ b/libavformat/mov.c
@@ -6598,15 +6598,149 @@ static int mov_read_dfla(MOVContext *c, AVIOContext *pb, MOVAtom atom)
return 0;
}
-static int cenc_decrypt(MOVContext *c, MOVStreamContext *sc, AVEncryptionInfo *sample, uint8_t *input, int size)
+static int cenc_scheme_decrypt(MOVContext *c, MOVStreamContext *sc, AVEncryptionInfo *sample, uint8_t *input, int size)
{
int i, ret;
+ int bytes_of_protected_data;
+ int partially_encrypted_block_size;
+ uint8_t *partially_encrypted_block;
+ uint8_t block[16];
- if (sample->scheme != MKBETAG('c','e','n','c') || sample->crypt_byte_block != 0 || sample->skip_byte_block != 0) {
- av_log(c->fc, AV_LOG_ERROR, "Only the 'cenc' encryption scheme is supported\n");
- return AVERROR_PATCHWELCOME;
+ if (!sc->cenc.aes_ctr) {
+ /* initialize the cipher */
+ sc->cenc.aes_ctr = av_aes_ctr_alloc();
+ if (!sc->cenc.aes_ctr) {
+ return AVERROR(ENOMEM);
+ }
+
+ ret = av_aes_ctr_init(sc->cenc.aes_ctr, c->decryption_key);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+
+ av_aes_ctr_set_full_iv(sc->cenc.aes_ctr, sample->iv);
+
+ if (!sample->subsample_count) {
+ /* decrypt the whole packet */
+ av_aes_ctr_crypt(sc->cenc.aes_ctr, input, input, size);
+ return 0;
+ }
+
+ partially_encrypted_block_size = 0;
+
+ for (i = 0; i < sample->subsample_count; i++) {
+ if (sample->subsamples[i].bytes_of_clear_data + sample->subsamples[i].bytes_of_protected_data > size) {
+ av_log(c->fc, AV_LOG_ERROR, "subsample size exceeds the packet size left\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ /* skip the clear bytes */
+ input += sample->subsamples[i].bytes_of_clear_data;
+ size -= sample->subsamples[i].bytes_of_clear_data;
+
+ /* decrypt the encrypted bytes */
+
+ if (partially_encrypted_block_size) {
+ memcpy(block, partially_encrypted_block, partially_encrypted_block_size);
+ memcpy(block+partially_encrypted_block_size, input, 16-partially_encrypted_block_size);
+ av_aes_ctr_crypt(sc->cenc.aes_ctr, block, block, 16);
+ memcpy(partially_encrypted_block, block, partially_encrypted_block_size);
+ memcpy(input, block+partially_encrypted_block_size, 16-partially_encrypted_block_size);
+ input += 16-partially_encrypted_block_size;
+ size -= 16-partially_encrypted_block_size;
+ bytes_of_protected_data = sample->subsamples[i].bytes_of_protected_data - (16-partially_encrypted_block_size);
+ } else {
+ bytes_of_protected_data = sample->subsamples[i].bytes_of_protected_data;
+ }
+
+ if (i < sample->subsample_count-1) {
+ int num_of_encrypted_blocks = bytes_of_protected_data/16;
+ partially_encrypted_block_size = bytes_of_protected_data%16;
+ if (partially_encrypted_block_size)
+ partially_encrypted_block = input + 16*num_of_encrypted_blocks;
+ av_aes_ctr_crypt(sc->cenc.aes_ctr, input, input, 16*num_of_encrypted_blocks);
+ } else {
+ av_aes_ctr_crypt(sc->cenc.aes_ctr, input, input, bytes_of_protected_data);
+ }
+
+ input += bytes_of_protected_data;
+ size -= bytes_of_protected_data;
+ }
+
+ if (size > 0) {
+ av_log(c->fc, AV_LOG_ERROR, "leftover packet bytes after subsample processing\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ return 0;
+}
+
+static int cbc1_scheme_decrypt(MOVContext *c, MOVStreamContext *sc, AVEncryptionInfo *sample, uint8_t *input, int size)
+{
+ int i, ret;
+ int num_of_encrypted_blocks;
+ uint8_t iv[16];
+
+ if (!sc->cenc.aes_ctx) {
+ /* initialize the cipher */
+ sc->cenc.aes_ctx = av_aes_alloc();
+ if (!sc->cenc.aes_ctx) {
+ return AVERROR(ENOMEM);
+ }
+
+ ret = av_aes_init(sc->cenc.aes_ctx, c->decryption_key, 16 * 8, 1);
+ if (ret < 0) {
+ return ret;
+ }
}
+ memcpy(iv, sample->iv, 16);
+
+ /* whole-block full sample encryption */
+ if (!sample->subsample_count) {
+ /* decrypt the whole packet */
+ av_aes_crypt(sc->cenc.aes_ctx, input, input, size/16, iv, 1);
+ return 0;
+ }
+
+ for (i = 0; i < sample->subsample_count; i++) {
+ if (sample->subsamples[i].bytes_of_clear_data + sample->subsamples[i].bytes_of_protected_data > size) {
+ av_log(c->fc, AV_LOG_ERROR, "subsample size exceeds the packet size left\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ if (sample->subsamples[i].bytes_of_protected_data % 16) {
+ av_log(c->fc, AV_LOG_ERROR, "subsample BytesOfProtectedData is not a multiple of 16\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ /* skip the clear bytes */
+ input += sample->subsamples[i].bytes_of_clear_data;
+ size -= sample->subsamples[i].bytes_of_clear_data;
+
+ /* decrypt the encrypted bytes */
+ num_of_encrypted_blocks = sample->subsamples[i].bytes_of_protected_data/16;
+ if (num_of_encrypted_blocks > 0) {
+ av_aes_crypt(sc->cenc.aes_ctx, input, input, num_of_encrypted_blocks, iv, 1);
+ }
+ input += sample->subsamples[i].bytes_of_protected_data;
+ size -= sample->subsamples[i].bytes_of_protected_data;
+ }
+
+ if (size > 0) {
+ av_log(c->fc, AV_LOG_ERROR, "leftover packet bytes after subsample processing\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ return 0;
+}
+
+static int cens_scheme_decrypt(MOVContext *c, MOVStreamContext *sc, AVEncryptionInfo *sample, uint8_t *input, int size)
+{
+ int i, ret, rem_bytes;
+ uint8_t *data;
+
if (!sc->cenc.aes_ctr) {
/* initialize the cipher */
sc->cenc.aes_ctr = av_aes_ctr_alloc();
@@ -6622,10 +6756,14 @@ static int cenc_decrypt(MOVContext *c, MOVStreamContext *sc, AVEncryptionInfo *s
av_aes_ctr_set_full_iv(sc->cenc.aes_ctr, sample->iv);
+ /* whole-block full sample encryption */
if (!sample->subsample_count) {
/* decrypt the whole packet */
av_aes_ctr_crypt(sc->cenc.aes_ctr, input, input, size);
return 0;
+ } else if (!sample->crypt_byte_block && !sample->skip_byte_block) {
+ av_log(c->fc, AV_LOG_ERROR, "pattern encryption is not present in 'cens' scheme\n");
+ return AVERROR_INVALIDDATA;
}
for (i = 0; i < sample->subsample_count; i++) {
@@ -6639,7 +6777,18 @@ static int cenc_decrypt(MOVContext *c, MOVStreamContext *sc, AVEncryptionInfo *s
size -= sample->subsamples[i].bytes_of_clear_data;
/* decrypt the encrypted bytes */
- av_aes_ctr_crypt(sc->cenc.aes_ctr, input, input, sample->subsamples[i].bytes_of_protected_data);
+ data = input;
+ rem_bytes = sample->subsamples[i].bytes_of_protected_data;
+ while (rem_bytes > 0) {
+ if (rem_bytes < 16*sample->crypt_byte_block) {
+ break;
+ }
+ av_aes_ctr_crypt(sc->cenc.aes_ctr, data, data, 16*sample->crypt_byte_block);
+ data += 16*sample->crypt_byte_block;
+ rem_bytes -= 16*sample->crypt_byte_block;
+ data += FFMIN(16*sample->skip_byte_block, rem_bytes);
+ rem_bytes -= FFMIN(16*sample->skip_byte_block, rem_bytes);
+ }
input += sample->subsamples[i].bytes_of_protected_data;
size -= sample->subsamples[i].bytes_of_protected_data;
}
@@ -6652,6 +6801,88 @@ static int cenc_decrypt(MOVContext *c, MOVStreamContext *sc, AVEncryptionInfo *s
return 0;
}
+static int cbcs_scheme_decrypt(MOVContext *c, MOVStreamContext *sc, AVEncryptionInfo *sample, uint8_t *input, int size)
+{
+ int i, ret, rem_bytes;
+ uint8_t iv[16];
+ uint8_t *data;
+
+ if (!sc->cenc.aes_ctx) {
+ /* initialize the cipher */
+ sc->cenc.aes_ctx = av_aes_alloc();
+ if (!sc->cenc.aes_ctx) {
+ return AVERROR(ENOMEM);
+ }
+
+ ret = av_aes_init(sc->cenc.aes_ctx, c->decryption_key, 16 * 8, 1);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+
+ /* whole-block full sample encryption */
+ if (!sample->subsample_count) {
+ /* decrypt the whole packet */
+ memcpy(iv, sample->iv, 16);
+ av_aes_crypt(sc->cenc.aes_ctx, input, input, size/16, iv, 1);
+ return 0;
+ } else if (!sample->crypt_byte_block && !sample->skip_byte_block) {
+ av_log(c->fc, AV_LOG_ERROR, "pattern encryption is not present in 'cbcs' scheme\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ for (i = 0; i < sample->subsample_count; i++) {
+ if (sample->subsamples[i].bytes_of_clear_data + sample->subsamples[i].bytes_of_protected_data > size) {
+ av_log(c->fc, AV_LOG_ERROR, "subsample size exceeds the packet size left\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ /* skip the clear bytes */
+ input += sample->subsamples[i].bytes_of_clear_data;
+ size -= sample->subsamples[i].bytes_of_clear_data;
+
+ /* decrypt the encrypted bytes */
+ memcpy(iv, sample->iv, 16);
+ data = input;
+ rem_bytes = sample->subsamples[i].bytes_of_protected_data;
+ while (rem_bytes > 0) {
+ if (rem_bytes < 16*sample->crypt_byte_block) {
+ break;
+ }
+ av_aes_crypt(sc->cenc.aes_ctx, data, data, sample->crypt_byte_block, iv, 1);
+ data += 16*sample->crypt_byte_block;
+ rem_bytes -= 16*sample->crypt_byte_block;
+ data += FFMIN(16*sample->skip_byte_block, rem_bytes);
+ rem_bytes -= FFMIN(16*sample->skip_byte_block, rem_bytes);
+ }
+ input += sample->subsamples[i].bytes_of_protected_data;
+ size -= sample->subsamples[i].bytes_of_protected_data;
+ }
+
+ if (size > 0) {
+ av_log(c->fc, AV_LOG_ERROR, "leftover packet bytes after subsample processing\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ return 0;
+}
+
+static int cenc_decrypt(MOVContext *c, MOVStreamContext *sc, AVEncryptionInfo *sample, uint8_t *input, int size)
+{
+ if (sample->scheme == MKBETAG('c','e','n','c') && !sample->crypt_byte_block && !sample->skip_byte_block) {
+ return cenc_scheme_decrypt(c, sc, sample, input, size);
+ } else if (sample->scheme == MKBETAG('c','b','c','1') && !sample->crypt_byte_block && !sample->skip_byte_block) {
+ return cbc1_scheme_decrypt(c, sc, sample, input, size);
+ } else if (sample->scheme == MKBETAG('c','e','n','s')) {
+ return cens_scheme_decrypt(c, sc, sample, input, size);
+ } else if (sample->scheme == MKBETAG('c','b','c','s')) {
+ return cbcs_scheme_decrypt(c, sc, sample, input, size);
+ } else {
+ av_log(c->fc, AV_LOG_ERROR, "invalid encryption scheme\n");
+ return AVERROR_INVALIDDATA;
+ }
+}
+
static int cenc_filter(MOVContext *mov, AVStream* st, MOVStreamContext *sc, AVPacket *pkt, int current_index)
{
MOVFragmentStreamInfo *frag_stream_info;
@@ -6666,7 +6897,9 @@ static int cenc_filter(MOVContext *mov, AVStream* st, MOVStreamContext *sc, AVPa
// Note this only supports encryption info in the first sample descriptor.
if (mov->fragment.stsd_id == 1) {
if (frag_stream_info->encryption_index) {
- encrypted_index = current_index - frag_stream_info->index_entry;
+ if (!current_index && frag_stream_info->index_entry)
+ sc->cenc.frag_index_entry_base = frag_stream_info->index_entry;
+ encrypted_index = current_index - (frag_stream_info->index_entry - sc->cenc.frag_index_entry_base);
encryption_index = frag_stream_info->encryption_index;
} else {
encryption_index = sc->cenc.encryption_index;